「prolog勉強プロジェクトオイラー11~20」の編集履歴(バックアップ)一覧に戻る

prolog勉強プロジェクトオイラー11~20 - (2013/12/15 (日) 06:01:23) のソース

小学校の算数までしかできないと巷で評判の堀江伸一さんが、プロジェクトオイラーの問題にprolog言語で挑戦してみました。
ローカルな言語が集まるプロジェクトオイラーですが流石にprologで解いてるのは私だけかな、チェックはしてないけど。



*Problem 11 「格子内の最大の積」 †
http://odz.sakura.ne.jp/projecteuler/index.php?cmd=read&page=Problem%2011
20*20の格子に数字が入ってるものが与えられる。
上下左右斜めの連続する4つの数字の積の最大値を求めよ。

解法
こういう問題はPrologのありがたさが少しだけわかる。
失敗したらとりあえずエラー処理もいらずにバックトラックしてくれるというのはとても便利。


 dyxs(1,0).
 dyxs(0,1).
 dyxs(1,1).
 dyxs(-1,1).
 
 calc11(_,_,_,_,_,4,Result,Result):-!.
 calc11(Board,Y,X,DY,DX,N,Multi,Result):-
 	X1 is X+DX,
	Y1 is Y+DY,
	N1 is N+1,
	nth0(Y,Board,Row),
	nth0(X,Row,Num),
	Multi1 is Multi*Num,
	calc11(Board,Y1,X1,DY,DX,N1,Multi1,Result).
 search_exe(Board,Y,X,Mult):-
	dyxs(DY,DX),
	calc11(Board,Y,X,DY,DX,0,1,Mult).
 
 max_search([],Max,Max).
 max_search([X|Rest],Max,Result):-Max<X,!,max_search(Rest,X,Result).
 max_search([_|Rest],Max,Result):-max_search(Rest,Max,Result).
 
 search11(_,20,_,Max):-!,write(Max).
 search11(Board,Y,20,Max):-
	!,
	X1 is 0,
	Y1 is Y+1,
	search11(Board,Y1,X1,Max).
 
 search11(Board,Y,X,Max):-
	X1 is X+1,
	Y1 is Y,
	findall(Multi,search_exe(Board,Y,X,Multi),List),
	max_search([Max|List],0,Max1),
	search11(Board,Y1,X1,Max1).
 main11:-Board=[[ 8, 2,22,97,38,15, 0,40, 0,75, 4, 5, 7,78,52,12,50,77,91, 8],
	       [49,49,99,40,17,81,18,57,60,87,17,40,98,43,69,48, 4,56,62, 0],
	       [81,49,31,73,55,79,14,29,93,71,40,67,53,88,30, 3,49,13,36,65],
	       [52,70,95,23, 4,60,11,42,69,24,68,56, 1,32,56,71,37, 2,36,91],
	       [22,31,16,71,51,67,63,89,41,92,36,54,22,40,40,28,66,33,13,80],
	       [24,47,32,60,99, 3,45, 2,44,75,33,53,78,36,84,20,35,17,12,50],
	       [32,98,81,28,64,23,67,10,26,38,40,67,59,54,70,66,18,38,64,70],
	       [67,26,20,68, 2,62,12,20,95,63,94,39,63, 8,40,91,66,49,94,21],
	       [24,55,58, 5,66,73,99,26,97,17,78,78,96,83,14,88,34,89,63,72],
	       [21,36,23, 9,75, 0,76,44,20,45,35,14, 0,61,33,97,34,31,33,95],
	       [78,17,53,28,22,75,31,67,15,94, 3,80, 4,62,16,14, 9,53,56,92],
	       [16,39, 5,42,96,35,31,47,55,58,88,24, 0,17,54,24,36,29,85,57],
	       [86,56, 0,48,35,71,89, 7, 5,44,44,37,44,60,21,58,51,54,17,58],
	       [19,80,81,68, 5,94,47,69,28,73,92,13,86,52,17,77, 4,89,55,40],
	       [ 4,52, 8,83,97,35,99,16, 7,97,57,32,16,26,26,79,33,27,98,66],
	       [88,36,68,87,57,62,20,72, 3,46,33,67,46,55,12,32,63,93,53,69],
	       [ 4,42,16,73,38,25,39,11,24,94,72,18, 8,46,29,32,40,62,76,36],
	       [20,69,36,41,72,30,23,88,34,62,99,69,82,67,59,85,74, 4,36,16],
	       [20,73,35,29,78,31,90, 1,74,31,49,71,48,86,81,16,23,57, 5,54],
	       [ 1,70,54,71,83,51,54,69,16,92,33,48,61,43,52,1,89,19,67,48]],
	search11(Board,0,0,0).


*Problem 12 「高度整除三角数」 †
http://odz.sakura.ne.jp/projecteuler/index.php?cmd=read&page=Problem%2012
500個以上の約数を持つ最初の三角数を求めよという問題。
解法
なんだかコードが膨らんだ。

 yakusuu(X,N,1,1,2):-X<N*N,!.
 yakusuu(X,N,1,Multi,Multi):-
	X<N,!.
 
 yakusuu(X,N,M,Multi,Result):-
	0=:=X mod N,!,
	M1 is M+1,
	X1 is X//N,
	yakusuu(X1,N,M1,Multi,Result).
 yakusuu(X,N,M,Multi,Result):-
	N1 is N+1,
	Multi1 is Multi*M,
	yakusuu(X,N1,1,Multi1,Result).
 
 check(N,N1,N2,List):-
	nth0(N1,List,Y1),
	nth0(N2,List,Y2),
	N3 is (N*(N+1))/2,
	(500=<Y1*Y2->(write(N3),fail);true).
 calc12(N,List):-
	0=:=N mod 2,!,
	N1 is N/2,
	N2 is N+1,
	N3 is N+1,
	yakusuu(N,2,1,1,Y0),
	yakusuu(N2,2,1,1,Y1),
	append(List,[Y0,Y1],List1),
	check(N,N1,N2,List1),
	calc12(N3,List1).
 calc12(N,List):-
	N1 is N,
	N2 is (N+1)/2,
	N3 is N+1,
	check(N,N1,N2,List),
	calc12(N3,List).
 main12:-calc12(1,[0,1]).




*Problem 13 「大数の和」 †
http://odz.sakura.ne.jp/projecteuler/index.php?cmd=read&page=Problem%2013
50桁の整数100個を加算し、上位10ケタを取り出せという問題。
解法
C++ではひと手間かかりますが、メモリの許す限り何桁でも扱えるプログラム言語ではこの問題は問題として成立しません。
足して出てきた答えの上位10ケタを取り出せばそれでおしまいです。

 sums([],Sum,Sum).
 sums([X|Rest],Sum,Result):-Sum1 is Sum+X,sums(Rest,Sum1,Result).
 
 main13:- 
	Nums=[37107287533902102798797998220837590246510135740250,
 46376937677490009712648124896970078050417018260538,
 74324986199524741059474233309513058123726617309629,
 91942213363574161572522430563301811072406154908250,
 23067588207539346171171980310421047513778063246676,
 89261670696623633820136378418383684178734361726757,
 28112879812849979408065481931592621691275889832738,
 44274228917432520321923589422876796487670272189318,
 47451445736001306439091167216856844588711603153276,
 70386486105843025439939619828917593665686757934951,
 62176457141856560629502157223196586755079324193331,
 64906352462741904929101432445813822663347944758178,
 92575867718337217661963751590579239728245598838407,
 58203565325359399008402633568948830189458628227828,
 80181199384826282014278194139940567587151170094390,
 35398664372827112653829987240784473053190104293586,
 86515506006295864861532075273371959191420517255829,
 71693888707715466499115593487603532921714970056938,
 54370070576826684624621495650076471787294438377604,
 53282654108756828443191190634694037855217779295145,
 36123272525000296071075082563815656710885258350721,
 45876576172410976447339110607218265236877223636045,
 17423706905851860660448207621209813287860733969412,
 81142660418086830619328460811191061556940512689692,
 51934325451728388641918047049293215058642563049483,
 62467221648435076201727918039944693004732956340691,
 15732444386908125794514089057706229429197107928209,
 55037687525678773091862540744969844508330393682126,
 18336384825330154686196124348767681297534375946515,
 80386287592878490201521685554828717201219257766954,
 78182833757993103614740356856449095527097864797581,
 16726320100436897842553539920931837441497806860984,
 48403098129077791799088218795327364475675590848030,
 87086987551392711854517078544161852424320693150332,
 59959406895756536782107074926966537676326235447210,
 69793950679652694742597709739166693763042633987085,
 41052684708299085211399427365734116182760315001271,
 65378607361501080857009149939512557028198746004375,
 35829035317434717326932123578154982629742552737307,
 94953759765105305946966067683156574377167401875275,
 88902802571733229619176668713819931811048770190271,
 25267680276078003013678680992525463401061632866526,
 36270218540497705585629946580636237993140746255962,
 24074486908231174977792365466257246923322810917141,
 91430288197103288597806669760892938638285025333403,
 34413065578016127815921815005561868836468420090470,
 23053081172816430487623791969842487255036638784583,
 11487696932154902810424020138335124462181441773470,
 63783299490636259666498587618221225225512486764533,
 67720186971698544312419572409913959008952310058822,
 95548255300263520781532296796249481641953868218774,
 76085327132285723110424803456124867697064507995236,
 37774242535411291684276865538926205024910326572967,
 23701913275725675285653248258265463092207058596522,
 29798860272258331913126375147341994889534765745501,
 18495701454879288984856827726077713721403798879715,
 38298203783031473527721580348144513491373226651381,
 34829543829199918180278916522431027392251122869539,
 40957953066405232632538044100059654939159879593635,
 29746152185502371307642255121183693803580388584903,
 41698116222072977186158236678424689157993532961922,
 62467957194401269043877107275048102390895523597457,
 23189706772547915061505504953922979530901129967519,
 86188088225875314529584099251203829009407770775672,
 11306739708304724483816533873502340845647058077308,
 82959174767140363198008187129011875491310547126581,
 97623331044818386269515456334926366572897563400500,
 42846280183517070527831839425882145521227251250327,
 55121603546981200581762165212827652751691296897789,
 32238195734329339946437501907836945765883352399886,
 75506164965184775180738168837861091527357929701337,
 62177842752192623401942399639168044983993173312731,
 32924185707147349566916674687634660915035914677504,
 99518671430235219628894890102423325116913619626622,
 73267460800591547471830798392868535206946944540724,
 76841822524674417161514036427982273348055556214818,
 97142617910342598647204516893989422179826088076852,
 87783646182799346313767754307809363333018982642090,
 10848802521674670883215120185883543223812876952786,
 71329612474782464538636993009049310363619763878039,
 62184073572399794223406235393808339651327408011116,
 66627891981488087797941876876144230030984490851411,
 60661826293682836764744779239180335110989069790714,
 85786944089552990653640447425576083659976645795096,
 66024396409905389607120198219976047599490197230297,
 64913982680032973156037120041377903785566085089252,
 16730939319872750275468906903707539413042652315011,
 94809377245048795150954100921645863754710598436791,
 78639167021187492431995700641917969777599028300699,
 15368713711936614952811305876380278410754449733078,
 40789923115535562561142322423255033685442488917353,
 44889911501440648020369068063960672322193204149535,
 41503128880339536053299340368006977710650566631954,
 81234880673210146739058568557934581403627822703280,
 82616570773948327592232845941706525094512325230608,
 22918802058777319719839450180888072429661980811197,
 77158542502016545090413245809786882778948721859617,
 72107838435069186155435662884062257473692284509516,
 20849603980134001723930671666823555245252804609722,
 53503534226472524250874054075591789781264330331690],
	sums(Nums,0,Ans),
	write(Ans).





*Problem 14 「最長のコラッツ数列」 †
http://odz.sakura.ne.jp/projecteuler/index.php?cmd=read&page=Problem%2014
100万以下の数字から始まるコラッツ数列のうち数列が最も長くなる初期値を答えよという問題。
そのまま全探索。
計算速度を稼ぐには2分木にコラッツ数列の結果を登録するとよいのだがそこまでしなくてもそれなりの時間で答えが出てくる。
とはいえstd::setすらないPrologはこういうとき不便だ。


 collatu(1,Len,Len):-!.
 collatu(N,Len,Result):-
	(0=:=N mod 2->N1 is N//2;N1 is 3*N+1),
	Len1 is Len+1,
	collatu(N1,Len1,Result).
 search(1000000,MaxLen,Ans):-!,write([MaxLen,Ans]).
 search(N,MaxLen,Ans):-
	collatu(N,1,Len),
	(MaxLen<Len->
	(MaxLen1 is Len,Ans1 is N);
	(MaxLen1 is MaxLen,Ans1 is Ans)),
	N1 is N+1,
	search(N1,MaxLen1,Ans1).
 main14:-search(1,1,1).





*Problem 15 「格子経路」 †
http://odz.sakura.ne.jp/projecteuler/index.php?cmd=read&page=Problem%2015
20*20マスのマス目の格子を左上から右下まで行く道は何通り?
解法
小学生がやる方法でそのまま解。

 calc_row15(_,[],[]):-!.
 calc_row15(N,[X|Rest],[N1|Result]):-
	N1 is N+X,
	calc_row15(N1,Rest,Result).
 
 calc15(21,Row):-!,reverse(Row,RowRev),[Ans|_]=RowRev,write(Ans).
 calc15(N,Row):-
	N1 is N+1,
	calc_row15(0,Row,Row1),
	calc15(N1,Row1).
 main15:-calc15(0,[1,0,0,0,0,
		  0,0,0,0,0,
		  0,0,0,0,0,
		  0,0,0,0,0,
		  0]).



*Problem 16 「累乗の各桁の和」 †
http://odz.sakura.ne.jp/projecteuler/index.php?cmd=read&page=Problem%2016
2^1000の各桁の和を求めよという問題。
そのまま処理するだけです。

 keta_sum16(0,Ans,Ans):-!.
 keta_sum16(N,Sum,Result):-
	Sum1 is Sum+N mod 10,
	N1 is N//10,
	keta_sum16(N1,Sum1,Result).
 
 main16:-N is 2^1000,keta_sum16(N,0,Ans),write(Ans).




*Problem 17 「数字の文字数」 †
http://odz.sakura.ne.jp/projecteuler/index.php?cmd=read&page=Problem%2017
1~1000までの数をイギリス英語表記した時、それからハイフンとスペースを除いた文字数を答えよ。
解法
地道に組み合わせを計算します。

 len(0,0).
 len(1,3).
 len(2,3).
 len(3,5).
 len(4,4).
 len(5,4).
 len(6,3).
 len(7,5).
 len(8,5).
 len(9,4).
 len(10,3).
 len(11,6).
 len(12,6).
 len(13,8).
 len(14,8).
 len(15,7).
 len(16,7).
 len(17,9).
 len(18,8).
 len(19,8).
 len(20,6).
 len(30,6).
 len(40,5).
 len(50,5).
 len(60,5).
 len(70,7).
 len(80,6).
 len(90,6).
 len(100,7).
 
 calc10(N,Len):-
	N<20,!,len(N,Len).
 calc10(N,Len):-
	0=:= N mod 10,!,
	len(N,Len).
 calc10(N,Len):-
	N1 is N mod 10,
        N2 is (N //10)*10,
	len(N1,L1),
	len(N2,L2),
	Len is L1+L2.
 calc(N,Len):-
	N100 is N//100,
	len(N100,L1),
	(0<L1->L2 is L1+7;L2 is L1),
	N10 is N mod 100,
	calc10(N10,L3),
	((0<L2,0<L3)->Add is 3;Add is 0),
	Len is L2+L3+Add.
 saiki(1000,Sum):-!,write(Sum).
 saiki(N,Sum):-
	calc(N,Len),
	Sum1 is Sum+Len,
	N1 is N+1,
	saiki(N1,Sum1).
  
 calc1000:-
 	saiki(1,11).




*Problem 18 「最大経路の和 その1」 †
http://projecteuler.net/problem=18
パスカルの三角形風味に数字表が与えられるのでその表を上から下までたどった時経路にある数の和の最大値を求めよ。
解法
簡単な問題なのでそのままです。
固定長リストでないという便利さは少しうれしいですね。

 main:-Nums=
 [[95,64],
 [17,47,82],
 [18,35,87,10],
 [20,04,82,47,65],
 [19,01,23,75,03,34],
 [88,02,77,73,07,63,67],
 [99,65,04,28,06,16,70,92],
 [41,41,26,56,83,40,80,70,33],
 [41,48,72,33,47,32,37,16,94,29],
 [53,71,44,65,25,43,91,52,97,51,14],
 [70,11,33,28,77,73,17,78,39,68,17,57],
 [91,71,52,38,17,14,91,43,58,50,27,29,48],
 [63,66,04,68,89,53,67,30,73,16,69,87,40,31],
 [04,62,98,27,23,09,70,98,73,93,38,53,60,04,23]],
 calc([75],Nums).
 
 max([],Max,Max):-!.
 max([X|Rest],Max,Result):-Max<X,!,max(Rest,X,Result).
 max([_|Rest],Max,Result):-max(Rest,Max,Result).
 calc_row([],_,R1,[R1]).
 calc_row([X|OldRow],[L,R|NowRow],TempL,[L2|Result]):-
	L1 is X+L,
	R1 is X+R,
	(L1<TempL->L2 is TempL;L2 is L1),
	calc_row(OldRow,[R|NowRow],R1,Result).
 
 calc(OldRow,[]):-max(OldRow,0,Ans),write(Ans).
 calc(OldRow,[NowRow|Rest]):-
	calc_row(OldRow,NowRow,0,NextRow),
	write(NextRow),nl,
        calc(NextRow,Rest).



*問い19 Problem 19 「日曜日の数え上げ」 †
http://odz.sakura.ne.jp/projecteuler/index.php?cmd=read&page=Problem%2019
1901年1月1日から2000年12月31日までの間の月初めに何回日曜日があるかを答える問題。
解法
Wikiにある曜日の公式をそのまま使います。

 toWeek(Y,M,D,Result):-
	(M<3->(M1 is M+12,Y1 is Y-1);(M1 is M,Y1 is Y)),
	Result is (Y1+Y1//4-Y1//100+Y1//400+(13*M1+8)//5+D) mod 7.

 search(2000,13,Sum):-!,write(Sum).
 search(Y,13,Sum):-!,
	Y1 is Y+1,
	search(Y1,1,Sum).
 search(Y,M,Sum):-
	M1 is M+1,
	(toWeek(Y,M,1,0)->Sum1 is Sum+1;Sum1 is Sum),
	search(Y,M1,Sum1).
 main:-search(1901,1,0).




*Problem 20 「階乗の数字和」 †
http://odz.sakura.ne.jp/projecteuler/index.php?cmd=read&page=Problem%2020
100!の各桁の和を求めよという問題。
そのまま実装。
 keta_sum(0,Sum):-!,write(Sum).
 keta_sum(X,Sum):-Sum1 is Sum+X mod 10,X1 is X//10,
	keta_sum(X1,Sum1).

 saiki(101,Sum):-keta_sum(Sum,0).
 saiki(N,Sum):-
	N1 is N+1,
	Sum1 is Sum*N,
	saiki(N1,Sum1).

 main:-saiki(1,1).