Google ClassroomGoogle Classroom
GeoGebraGeoGebra Classroom

factをやろう

1.再帰なしに階乗計算できる?

このワークシートはMath by Codeの一部です。 <factorial> フィボナッチ数列の一般項は、意外なことに、 黄金比にかかわる無理数を使った式になる。 くわしい計算はしませんが、 漸化式から特性方程式を作り一般項を作ると無理数が残るんだ。 それに対してNの階乗fact(N)は1からNまでの積だから、一般項は単純だ。 だから、再帰関数でも表せるけど、forで十分かけるね。
#再帰を使うならキャッシュを使おう。 #================== from functools import cache @cache def factorial(n): return n * factorial(n-1) if n else 1 factorial(100) #再帰なしなら、どんどんかけ算して上書きしよう。 #================== def fact(n): res=1 for i in range(1,n+1): res *=i return res fact(100) #================== [OUT] 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

階乗はIterationListかProductか?

geogbraでは fibを再帰を使わずに素早く求めることができた。そのときの手段がIterationListだった。 同じようにIterationListやIterationでfactも再帰を使わず求めることができる。 geogebraにはProductというコマンドがあったね。 1から100までの数列をlst=Sequence(100)で作っておき、 それの積をProduct(lst)するとfactorial(100)が計算できる。 でも、有効数字には課題が残った。

2.再帰と反復

pythonにはmathパッケージの関数一覧をリスト表示すると、factorialという関数がすでにありました。 [IN] #=========== import math print(dir(math)) #============ [OUT] ['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'cbrt', 'ceil', 'comb', 'copysign', 'cos', 'cosh', 'degrees', 'dist', 'e', 'erf', 'erfc', 'exp', 'exp2', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'isqrt', 'lcm', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'nextafter', 'perm', 'pi', 'pow', 'prod', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'sumprod', 'tan', 'tanh', 'tau', 'trunc', 'ulp'] [IN] #=========== from math import factorial as fact fact(100) #============ factorial関数だけを factという別名をつけて、簡単に使えます。 パッケージがあるときは、何でも自力で作るのではなく、それに乗っかるというときもあってよいでしょう。 算数、数学と同じようにコード開発でも、 知識・技術に乗っかって秒殺する快感とかエレガントさというのもあるでしょう。 しかし、それで終わってしまうと、思考が深まりません。 フィボナッチ数列よりも扱いやすいということで、階乗計算を使って、 くり返し(反復iteration)と自分を呼び出す(再帰recurrsion)の関係をさぐってみましょう。 #[IN]C++ #===================== int factorial(int n) { int result = 1; for (int i = 1; i <= n; ++i) { result *= i;  }  return result; } C++でforで反復するとこんな風になるでしょう。 これはPythonでもjuliaでも同じようなものです。 <productの中身> haskellでは #================= fact n = product [1..n] #================= のように、product関数に1からnのリストをわたすだけで終了です。 ではproduct関数ってどうなっているんでしょう。 リストが空なら1を返し、先頭に残りのproductをかければいいですね。 だから、再帰関数を使うと、 product [] =1 product (n: ns) = n * product ns と定義できます。 数であれば何でもproductできるという一般化をしましょう。 ghciに尋ねると ghci>:info product type Foldable :: (* -> *) -> Constraint class Foldable t where ... product :: Num a => t a -> a -- Defined in ‘Data.Foldable’ ghci> :t product product :: (Foldable t, Num a) => t a -> a と答えてきます。 tが畳み込みのできるクラス、aが数のクラスで、数リストt aから数aを返す。 だから、次のようにも定義できるのです。 product = foldl (*) 1 <foldlを使おう> haskellでは、foldlの中はどうなっているのでしょうか? ghci> :t foldl foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b tが畳み込みのできるクラス、()内は2項演算と数リストt aがあれば、 数aのうち演算やリストの左側をbとして、畳み込みを続ける。 という型の説明です。これを再帰でかくと次のようになります。 foldl f v [ ] = v foldl f v (x: xs) = foldl f (f v x ) xs vを初期値として、演算をfとする。 リストが[ ]になったらvを返す。(ベースケース) リストの左をx、残りをxsとしたら、左xにfをしたものをvとして 残りxsをリストとする。 (再帰ケース) foldlを使うと、2項演算をリストにくり返し適用するものなら、 簡単に関数定義できますね。 (例) 関数そのものの定義だから、引数としてのリストは省けます。 sum = foldl (+) 0 product = foldl (*) 1 or = foldl (∨) False and = foldl (∧) True legth = foldl (\n _ -> n+1) 0 質問:ほかの言語でのfoldlの活用はどうでしょうか。 haskellとはちがって、引数はかっこで閉じる必要があるので、見かけは代わります。 #[IN] julia #======================= function sum(lst) return foldl(+,lst) end function product(lst) return foldl(*,lst) end lst=[1,2,3,4,5] println("lst=$lst, sum(lst)=$(sum(lst)),product(lst)=$(product(lst))") #===================================================== [OUT]lst=[1, 2, 3, 4, 5], sum(lst)=15,product(lst)=120 ちゃんと動きました。 次のようにjuliaで数学の関数定義風にかいても動きました。 #[IN] julia #======================= Sum(lst) = foldl(+,lst) Product(lst)= foldl(*,lst) lst=[1,2,3,4,5] println("lst=$lst, Sum(lst)=$(Sum(lst)),Product(lst)=$(Product(lst))") #===================================================== [OUT] lst=[1, 2, 3, 4, 5], sum(lst)=15,product(lst)=120 pythonにはfoldlという名前の関数はないが、実態がfoldlと同じreduce関数がある。 ただし、パッケージfunctoolsにあるからインポートする。別名foldlをつけてみた。 残念ながら、reduceの引数は(演算、リスト)ではなく(関数、リスト)だ。 だから、+,*のまま渡すのではなく、関数にして渡す。 わざわざそのために、関数を外に作るのも手間だから、中にラムダをつかった無名関数 にしている。 pythonは、ラムダ式の発想を忠実に再現しているが、 関数の関数を作ったりしようとすると、簡単な内容なのに難解に見えてしまうのが難点だ。 #[IN] python #======================= from functools import reduce as foldl Sum =lambda lst: foldl(lambda x,y:x+y,lst) Product = lambda lst: foldl(lambda x,y:x*y,lst) lst=[1,2,3,4,5] print(f"lst={lst}, Sum(lst)={Sum(lst)},Product(lst)={Product(lst)}") lst=[1, 2, 3, 4, 5], Sum(lst)=15,Product(lst)=120 #===================================================== [[OUT] lst=[1, 2, 3, 4, 5], sum(lst)=15,product(lst)=120 geogebraの数式、数式処理(CAS)には、関数を自作する機能はあるが、fold,reduceはない。 Iteration,IterationListはあくまでも初期値に対する反復計算なので、初期値以外を途中から渡すことはできない。漸化式の発想でしょうね。 しかし、sumも、productもすでにあるので、それを使えばよいでしょう。というところか。 また、数式処理(CAS)を使うと、nPr,nCrがあるので、わざわざfactorialを作るところからやらなくても、 すぐに順列、組み合わせがすぐに使えるという実用性を重視している感があるね。
質問:rustでfactorialを計算するにはどうしますか。 もちろんC++のように反復計算できます。 多桁整数に対応できるように、 Cargo.tomlの[dependencies]に num-bigint = "0.4" を記入しておきます。 (今後0.5以上のバージョンになるかも) 1を多桁整数として扱うために、use std::str::FromStr; も宣言しておきましょう。 階乗計算を順次表示するためにはfact関数の中にprintln!()をかきます。 最後の結果だけ表示するにはmain関数の中でprintln!()をかけばいいね。 ただ、rustの場合は変数を盗んではいけないので、&をつけて借用して 表示します。 基本型のデータは所有権は気にならないですが、構造体とかBigIntなど メモリーを定型スタックするのではなく、ヒープメモリーの領域を 使う場合は、所有権を意識して代入や表示でも借用、参照にしましょう。 ansのように、入れ物として使ったけど、あと使わない変数は前に‗をつけます。 また、数の変換操作などのあとの.unwrap()は成功したらラップをはがす定型句です。 これで、その結果をエラーなしに代入できます。 また、for文のi は多桁整数ではないから、これを変換してからかけましょう。 [IN]rust use num_bigint::{BigInt, ToBigInt}; use std::str::FromStr; fn main() { let idx = 100; let _ans = fact(idx); } fn fact(num:i64) -> BigInt { let mut result = BigInt::from_str("1").unwrap(); for i in 1..=num { result = result * i.to_bigint().unwrap(); println!("{}番目:{}",&i, &result); } result } [OUT] 1番目:1 2番目:2 3番目:6 4番目:24 5番目:120 6番目:720 7番目:5040 8番目:40320 9番目:362880 10番目:3628800 11番目:39916800 12番目:479001600 13番目:6227020800 14番目:87178291200 15番目:1307674368000 16番目:20922789888000 17番目:355687428096000 18番目:6402373705728000 19番目:121645100408832000 20番目:2432902008176640000 21番目:51090942171709440000 22番目:1124000727777607680000 23番目:25852016738884976640000 24番目:620448401733239439360000 25番目:15511210043330985984000000 26番目:403291461126605635584000000 27番目:10888869450418352160768000000 28番目:304888344611713860501504000000 29番目:8841761993739701954543616000000 30番目:265252859812191058636308480000000 31番目:8222838654177922817725562880000000 32番目:263130836933693530167218012160000000 33番目:8683317618811886495518194401280000000 34番目:295232799039604140847618609643520000000 35番目:10333147966386144929666651337523200000000 36番目:371993326789901217467999448150835200000000 37番目:13763753091226345046315979581580902400000000 38番目:523022617466601111760007224100074291200000000 39番目:20397882081197443358640281739902897356800000000 40番目:815915283247897734345611269596115894272000000000 41番目:33452526613163807108170062053440751665152000000000 42番目:1405006117752879898543142606244511569936384000000000 43番目:60415263063373835637355132068513997507264512000000000 44番目:2658271574788448768043625811014615890319638528000000000 45番目:119622220865480194561963161495657715064383733760000000000 46番目:5502622159812088949850305428800254892961651752960000000000 47番目:258623241511168180642964355153611979969197632389120000000000 48番目:12413915592536072670862289047373375038521486354677760000000000 49番目:608281864034267560872252163321295376887552831379210240000000000 50番目:30414093201713378043612608166064768844377641568960512000000000000 51番目:1551118753287382280224243016469303211063259720016986112000000000000 52番目:80658175170943878571660636856403766975289505440883277824000000000000 53番目:4274883284060025564298013753389399649690343788366813724672000000000000 54番目:230843697339241380472092742683027581083278564571807941132288000000000000 55番目:12696403353658275925965100847566516959580321051449436762275840000000000000 56番目:710998587804863451854045647463724949736497978881168458687447040000000000000 57番目:40526919504877216755680601905432322134980384796226602145184481280000000000000 58番目:2350561331282878571829474910515074683828862318181142924420699914240000000000000 59番目:138683118545689835737939019720389406345902876772687432540821294940160000000000000 60番目:8320987112741390144276341183223364380754172606361245952449277696409600000000000000 61番目:507580213877224798800856812176625227226004528988036003099405939480985600000000000000 62番目:31469973260387937525653122354950764088012280797258232192163168247821107200000000000000 63番目:1982608315404440064116146708361898137544773690227268628106279599612729753600000000000000 64番目:126886932185884164103433389335161480802865516174545192198801894375214704230400000000000000 65番目:8247650592082470666723170306785496252186258551345437492922123134388955774976000000000000000 66番目:544344939077443064003729240247842752644293064388798874532860126869671081148416000000000000000 67番目:36471110918188685288249859096605464427167635314049524593701628500267962436943872000000000000000 68番目:2480035542436830599600990418569171581047399201355367672371710738018221445712183296000000000000000 69番目:171122452428141311372468338881272839092270544893520369393648040923257279754140647424000000000000000 70番目:11978571669969891796072783721689098736458938142546425857555362864628009582789845319680000000000000000 71番目:850478588567862317521167644239926010288584608120796235886430763388588680378079017697280000000000000000 72番目:61234458376886086861524070385274672740778091784697328983823014963978384987221689274204160000000000000000 73番目:4470115461512684340891257138125051110076800700282905015819080092370422104067183317016903680000000000000000 74番目:330788544151938641225953028221253782145683251820934971170611926835411235700971565459250872320000000000000000 75番目:24809140811395398091946477116594033660926243886570122837795894512655842677572867409443815424000000000000000000 76番目:1885494701666050254987932260861146558230394535379329335672487982961844043495537923117729972224000000000000000000 77番目:145183092028285869634070784086308284983740379224208358846781574688061991349156420080065207861248000000000000000000 78番目:11324281178206297831457521158732046228731749579488251990048962825668835325234200766245086213177344000000000000000000 79番目:894618213078297528685144171539831652069808216779571907213868063227837990693501860533361810841010176000000000000000000 80番目:71569457046263802294811533723186532165584657342365752577109445058227039255480148842668944867280814080000000000000000000 81番目:5797126020747367985879734231578109105412357244731625958745865049716390179693892056256184534249745940480000000000000000000 82番目:475364333701284174842138206989404946643813294067993328617160934076743994734899148613007131808479167119360000000000000000000 83番目:39455239697206586511897471180120610571436503407643446275224357528369751562996629334879591940103770870906880000000000000000000 84番目:3314240134565353266999387579130131288000666286242049487118846032383059131291716864129885722968716753156177920000000000000000000 85番目:281710411438055027694947944226061159480056634330574206405101912752560026159795933451040286452340924018275123200000000000000000000 86番目:24227095383672732381765523203441259715284870552429381750838764496720162249742450276789464634901319465571660595200000000000000000000 87番目:2107757298379527717213600518699389595229783738061356212322972511214654115727593174080683423236414793504734471782400000000000000000000 88番目:185482642257398439114796845645546284380220968949399346684421580986889562184028199319100141244804501828416633516851200000000000000000000 89番目:16507955160908461081216919262453619309839666236496541854913520707833171034378509739399912570787600662729080382999756800000000000000000000 90番目:1485715964481761497309522733620825737885569961284688766942216863704985393094065876545992131370884059645617234469978112000000000000000000000 91番目:135200152767840296255166568759495142147586866476906677791741734597153670771559994765685283954750449427751168336768008192000000000000000000000 92番目:12438414054641307255475324325873553077577991715875414356840239582938137710983519518443046123837041347353107486982656753664000000000000000000000 93番目:1156772507081641574759205162306240436214753229576413535186142281213246807121467315215203289516844845303838996289387078090752000000000000000000000 94番目:108736615665674308027365285256786601004186803580182872307497374434045199869417927630229109214583415458560865651202385340530688000000000000000000000 95番目:10329978488239059262599702099394727095397746340117372869212250571234293987594703124871765375385424468563282236864226607350415360000000000000000000000 96番目:991677934870949689209571401541893801158183648651267795444376054838492222809091499987689476037000748982075094738965754305639874560000000000000000000000 97番目:96192759682482119853328425949563698712343813919172976158104477319333745612481875498805879175589072651261284189679678167647067832320000000000000000000000 98番目:9426890448883247745626185743057242473809693764078951663494238777294707070023223798882976159207729119823605850588608460429412647567360000000000000000000000 99番目:933262154439441526816992388562667004907159682643816214685929638952175999932299156089414639761565182862536979208272237582511852109168640000000000000000000000 100番目:93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
質問:rustでfactorialを計算するのにfoldなど使ってできますか。 もちろんhaskellのようにできます。 rustのイテレータクレートにはmap, filterの他にfold,rfold,reduceなどが あります。これに乗っかる手もありますね。 多桁整数に対応できるように、 Cargo.tomlの[depetndencies]に num-bigint = "0.4" を記入しておきます。 対象から要素へという順番で、詳細化していきましょう。 階乗計算の基点になる1を多桁にしておきます。 let one = BigInt::from_str("1").unwrap(); n!のnを100くらいにすると、 let n = 100; ここからが本番です。 レンジ(1..=n)の要素を多桁整数リストに変換しましょう。 (1..=n).map(|x| x.to_bigint().unwrap()) そのリストから取り出した要素xと累積(accumlator)をaccumとして、 対象.Iter().fold(|対象たち|対象たちへの操作)という順に描きます。 .fold(one, |accum, x| accum * x); その結果をresultに入れて表示します。 use num_bigint::{BigInt, ToBigInt}; use std::str::FromStr; fn main() { let n = 100; let one = BigInt::from_str("1").unwrap(); let result = (1..=n) .map(|x| x.to_bigint().unwrap()) .fold(one, |accum, x| accum * x); println!("{}", result); }
質問:もっと手軽にrustでイテレーション関数を使えませんか。 使えます。多桁整数でないのなら、範囲のあとに.sum(), .product()をつけるだけで、 たし算やかけ算を繰り返した結果を返します。 だから、次のようにして計算ができますよ。 [IN]rust fn main() {     let n:i64 = 20;     let sum20:i64 = (1..n).sum();     let fact20:i64 = (1..n).product();     println!("1から20までの和={}", sum20);     println!("1から20までの和={}", fact20); } [OUT] 1から20までの和=190 1から20までの和=121645100408832000