2014年3月15日土曜日

プロジェクトオイラー 問93

プロジェクトオイラーの問題をpythonでやってみます。
日本語翻訳サイトは、プロジェクトオイラー日本語 でネット検索です。

問93「算術式」
集合 {1, 2, 3, 4} の各数字をちょうど一度用い, また四則演算 (+, -, *, /) と括弧を使うことにより, 異なる正の整数を作ることができる.

例えば,
8 = (4 * (1 + 3)) / 2
14 = 4 * (3 + 1 / 2)
19 = 4 * (2 + 3) - 1
36 = 3 * 4 * (2 + 1)

12 + 34 のように数字をつなげることは許されないことに注意しよう.

集合 {1, 2, 3, 4} を使うと, 36 を最大とする 31 個の異なる数が得られる.
最初の表現できない数に会うまで, 1 から 28 の各数を得ることができる.

最長の連続した正の整数 1 から n の集合を得ることができる, 4 つの異なる数字 a < b < c < d を見つけよ. 答えを文字列 abcd として与えよ.






注意!!!
自力で解きたい人へ
以降の記述には解法に関するネタバレが含まれます。






私の回答例は以下の通りです。
def f():
	import itertools
	r = 0
	#括弧の位置  [0]a-[1]b[2]-[3]c[4]-d[5]
	kakko = [("(((","",")","",")",")"), ("(","",")","(","",")"), ("","(","","",")","")]
	for t in itertools.combinations(xrange(1, 10), 4):
		D = {}
		for b in itertools.permutations(t):
			f = [str(i)+".0" for i in b]
			for m in itertools.product("+-*/", repeat=3):
				for k in kakko:
					s0= k[0]+f[0]+m[0]+k[1]+f[1]+k[2]+m[1]+k[3]+f[2]+k[4]+m[2]+f[3]+k[5]
					try: s1 = eval(s0)
					except ZeroDivisionError: continue
					if int(s1)==s1: D[int(s1)] = s0

		for i in xrange(1, max(D.keys())+1):
			if i not in D.keys(): break

		if r<i-1: p, r, w = t, i-1, [D[j] for j in xrange(1,i)]

	p = "".join([str(i) for i in p])
	w = [s.replace(".0","")+"="+str(int(eval(s))) for s in w]
	return p,r,w

ans, vol, pat = f()
print "ans:",ans
print "vol:",vol
print "pat:",pat

総当りです。1分ルールには間に合いました。
式の文字列として作り出し、eval関数で計算式として計算します。
・9個の数字から異なる4つを選ぶパターンは、重複なし組合せで、9C4 = 126通り。
・4つの異なる数字の並び順パターンは、重複なし順列で、4P4 = 4! = 24通り。
・四則演算を3回行うパターン数は、重複あり順列で、4の3乗で64通り。
・数字が4個のとき、カッコのパターン数は並1べ順替えを考慮すると以下の3通り。
例えば以下のとおり。
(((9 - 5)- 2)- 1) = 9-5-2-1 = 1
  (9 - 5)-(2 - 1) = 9-5-2+1 = 3
   9 -(5 - 2)- 1  = 9-5+2-1 = 5
126x24x64x3 = 580,608通りを総当りで試しました。

文字型で式を作成した後、eval関数で計算式そのものに変換して実行します。
割り算記号がどこに来ても切り捨てられないように、全ての数字の後ろに「.0」を追加しておきます。
割り算でゼロ除算になる場合は、try-exceptでつかまえて次のケースに進みます。
計算結果の小数有無判定はint関数結果と同じ値になるかで判定します。
計算結果が正の整数値の場合、辞書(連想配列)Dに式そのものをためていきます。

連続判定では、リストDのキーに1からの連続値があるところi番目の1つ前、i-1番目まで連続です。
最長判定では、変数rに連続パターン件数をとっておき、最長かどうかを判定します。
求められている数字の組のパターンの他、連続個数とその式も表示します。

解答はこのすぐ下の行です。文字の色を白にしてます。選択状態にすると見えます。
ans: 1258
vol: 51
pat: ['(8-5)/(2+1)=1', '(8-5)-(2-1)=2', '(8-5)/(2-1)=3', '8-(5-2)-1=4','(((8-5)*2)-1)=5', '(8-5)*(2/1)=6', '(((8-5)*2)+1)=7', '(((8-5)+1)*2)=8', '(8-5)*(2+1)=9', '8+(5-2)-1=10', '8+(5-2)/1=11', '(8+5)-(2-1)=12', '(8+5)/(2-1)=13', '8+(5+2)-1=14', '8+(5+2)/1=15', '8+(5+2)+1=16', '8+(5*2)-1=17', '8+(5*2)/1=18', '8*(5/2)-1=19', '8*(5/2)/1=20', '8*(5/2)+1=21', '(8*2)+(5+1)=22', '8*(5-2)-1=23', '8*(5-2)/1=24', '8*(5-2)+1=25', '(8+5)*(2/1)=26', '(((8+5)*2)+1)=27', '(((8+5)+1)*2)=28', '(((8-2)*5)-1)=29', '8*(5-1)-2=30', '(((8-2)*5)+1)=31', '(((5-2)+1)*8)=32', '(((8-1)*5)-2)=33', '8*(5-1)+2=34', '(((8-2)+1)*5)=35', '(8-2)*(5+1)=36', '(((8*5)-2)-1)=37', '(8*5)-(2/1)=38', '(8*5)-(2-1)=39', '(8*5)/(2-1)=40', '(8*5)+(2-1)=41', '(8*5)+(2/1)=42', '(8*5)+(2+1)=43', '(((1/2)+5)*8)=44', '(((8+2)-1)*5)=45', '8*(5+1)-2=46', '(((8+1)*5)+2)=47', '(((5+2)-1)*8)=48', '(((8+2)*5)-1)=49', '8*(5+1)+2=50',  '(((8+2)*5)+1)=51']

2014年2月11日火曜日

プロジェクトオイラー 問92

プロジェクトオイラーの問題をpythonでやってみます。
日本語翻訳サイトは、プロジェクトオイラー日本語 でネット検索です。

問92「桁の2乗による連鎖」
各桁の2乗を足し合わせて新たな数を作ることを, 同じ数が現れるまで繰り返す.

例えば

44 → 32 → 13 → 10 → 1 → 1
85 → 89 → 145 → 42 → 20 → 4 → 16 → 37 → 58 → 89

のような列である. どちらも1か89で無限ループに陥っている. 
驚くことに, どの数から始めても最終的に1か89に到達する.

では, 10,000,000より小さい数で89に到達する数はいくつあるか.





注意!!!
自力で解きたい人へ
以降の記述には解法に関するネタバレが含まれます。






私の回答例は以下の通りです。
def f(n):
	P = [i*i for i in xrange(10)]
	s = len(str(n))
	m = 9*9*s
	
	L = [0]+[-1]*(m-1)
	L[1], L[89] = 0, 1
	for i in xrange(1, m):
		j = i
		while L[j]<0:
			j, r = 0, j
			while r:
				j += P[r%10]
				r //= 10
			L[i] = L[j]
			
	M = [0]*m
	for i in xrange(10): M[P[i]] = 1
	for u in xrange(2, s):
		W = [0]*m
		for i in xrange(m):
			for j in xrange(10):
				if P[j]<=i: W[i] += M[i-P[j]]
		M = W[:]
		
	return sum([i*j for i, j in zip(L, M)])
	
n = 10000000
print f(n)



1から10,000,000まで、桁の2乗和の連鎖を総当りで計算すると、とても1分ルールに合いません。

試行錯誤したところ、桁2乗和は1回計算するだけで値がずいぶんちいさくなることがわかりました。
桁数sのときの桁2乗和最大値mは9がs個並んだ数で、m = 9*9*s です。
7桁ならば、m = 9*9*7 = 567 です。

そこで、
・m以下の数は桁2乗和の連鎖を最後まで計算して89ループになるか判定しておく。
・桁2乗和は1回だけ計算しm以下にしたところで数える。
ことにしました。

・89ループ判定結果リストL
1からmまでの桁2乗和の連鎖を最後まで行い、89ループになる場合は1、その他は0を設定します。
-1で初期設定しているので、while L[j]<0として未設定が続く限り連鎖をたどります。
rのwhileループで、rの10で割った余りとして、下から1桁ずつ取り出して2乗和を累積します。
2乗の計算はいちいち行わず、2乗リストP[0,1,4,9,16...]を参照します。

・桁2乗和ごとの件数リストM
初期値として1桁数値の桁2乗和の位置(1,4,9...)に1を設定します。
2桁以上の数値については、1つ少ない桁数までの累積件数の、最上位の数値の2乗分だけ小さい位置にある値を、採用し累積していきます。
このようにしてだんだん桁数を増やしていきます。

こうして、
・89ループ判定結果リストL(89ループに達する数の位置に1が立ち、他は0が設定)
・桁2乗和を1回だけ計算した値ごとの件数リストM
の同じ位置の要素同士の積を求めることで89ループの部分を取り出します。
この総和が求める件数です。


解答はこのすぐ下の行です。文字の色を白にしてます。選択状態にすると見えます。
8581146

2014年2月1日土曜日

プロジェクトオイラー 問91

プロジェクトオイラーの問題をpythonでやってみます。
日本語翻訳サイトは、プロジェクトオイラー日本語 でネット検索です。

問91「整数座標における直角三角形の個数」
点P(x1, y1)と点Q(x2, y2)はともに整数係数の点であり, 原点O(0,0)と合わせてΔOPQをなす.

各係数が0と2の間にあるとき, すなわち0≦x1, y1, x2, y2≦2のとき, 直角三角形は14個できる:

では, 0≦x1, y1, x2, y2≦50のとき, 直角三角形は何個作れるか?





注意!!!
自力で解きたい人へ
以降の記述には解法に関するネタバレが含まれます。






私の回答例は以下の通りです。
def f(n):
	c = 0
	L = [(i, j) for i in xrange(n+1) for j in xrange(n+1)][1:]
	for x1, y1 in L:
		for x2, y2 in L:
			if x1==x2 and y1==y2: continue
			if not (x1*(x1-x2)+y1*(y1-y2)) \
			or not (x2*(x1-x2)+y2*(y1-y2)) \
			or not (x1*x2+y1*y2): c += 1
	return c//2

n = 50
print f(n)



2点P,Qの座標を総当りで作り出します。
0からnまでの数の重複を許す組み合わせをP, Qの座標候補リストLとします。
Lは内包表記に二重ループを入れてみました。
ただし、三角形なので3点O,P,Qはそれぞれ別の座標なので、まず点O(0,0)を除きます。
P(x1,y1),Q(x2,y2)のそれぞれの座標を二重ループで取得します。
まず点P,Qが同じ座標ならば三角形にならないので次の座標へ処理を進めます。

直角三角形か判定は、ベクトルの内積が0になることを利用します。
点Pが直角:ベクトルOPとベクトルQPの内積、x1*(x1-x2)+y1*(y1-y2) = 0
点Qが直角:ベクトルOQとベクトルQPの内積、x2*(x1-x2)+y2*(y1-y2) = 0
点Oが直角:ベクトルOPとベクトルOQの内積、x1*x2+y1*y2 = 0
数字の0は論理的にFalseなので、(not 内積値)で直角三角形であることを判定します。
行末の「\」は改行してもまだ同じ行と見なす継続行であることを示します。


解答はこのすぐ下の行です。文字の色を白にしてます。選択状態にすると見えます。
14234

2014年1月24日金曜日

プロジェクトオイラー 問90

プロジェクトオイラーの問題をpythonでやってみます。
日本語翻訳サイトは、プロジェクトオイラー日本語 でネット検索です。

問90「平方数を作る2つのさいころ」
立方体の6面それぞれに異なる数字(0から9)が書かれている.2番目の立方体も同様になっている.異なる位置に2つの立方体を隣り合わせることで様々な2桁の数を作ることができる.

例えば, 平方数である64も作ることができる:

事実, 両方の立方体の数字を注意深く選ぶと100以下のすべての平方数を示すことが可能である:
01, 04, 09, 16, 25, 36, 49, 64, そして 81.

例えば, これを実現する一つの方法としては {0, 5, 6, 7, 8, 9} を一方の立方体に, そして {1, 2, 3, 4, 8, 9} を他方の立方体に配置すればよい.
しかし, 6と9を逆さまに回転することを許すと
 {0, 5, 6, 7, 8, 9} と {1, 2, 3, 4, 6, 7} 
のような配列で9つすべての平方数を示す事ができる; 
そうでなければ09を得ることができない.

順番ではなくそれぞれの立方体の数字に着目して配列を区別する.

{1, 2, 3, 4, 5, 6} は {3, 6, 4, 1, 2, 5} と同じものとし
{1, 2, 3, 4, 5, 6} は {1, 2, 3, 4, 5, 9} と異なるものとする.

しかし6と9を逆さにすることを許すために, 最後の例で区別された両方の配列のかわりに, {1, 2, 3, 4, 5, 6, 9} という(要素数が7つに)拡張された配列を使用して2桁の数をつくることにする.

すべての平方数を表示し得る2つの立方体の異なる配列の組はいくつあるか.





注意!!!
自力で解きたい人へ
以降の記述には解法に関するネタバレが含まれます。






私の回答例は以下の通りです。
def g(L):
	if   (6 not in L) and (9 in L): L.append(6)
	elif (6 in L) and (9 not in L): L.append(9)
	return L
	
def f():
	import itertools
	L0, c = set([i**2 for i in xrange(1,10)]), 0
	for c1 in itertools.combinations(range(10), 6):
		c1 = g(list(c1))
		for c2 in itertools.combinations(range(10), 6):
			c2 = g(list(c2))
			M0 = [i*10+j for i in c1 for j in c2]
			M1 = [j*10+i for i in c1 for j in c2]
			if not L0-set(M0+M1): c+=1

	return c/2

print f()

まず平方数リストL0と、題意に合う状態をカウントするカウンターcを初期設定します。
このとき、平方数リストは後で集合としての差を計算するのでset関数で集合型にしておきます。
ループ内で1つ目のさいころの目の組の候補c1として、
itertoolsにある順列関数combinationsを使用し、10未満の自然数から重複を許さずに6個選んだ組み合わせを順に作り出し、
さらにそのループ内で2つ目のさいころの目の組の候補c2も同様に作り出します。
このとき関数g(L)にて、6か9のどちらかがあれば、このうちの無い方を追加しておきます。
このようにして作り出したc1とc2の候補のペアがで題意に合うか判定します。

判定は上記c1,c2の2つのリストから二重ループで回してとりだしたすべての要素の組をそれぞれ10の位、1の位とした値を作り出します。
そして、set関数で集合型にして、平方数の集合からこれを引いた差集合が空であれば、すべての平方数が作り出せたことになるので、カウンタを+1します。

点Pと点Qが入れ替わった直角三角形も別としてカウントしているため、
直角三角形の個数はその半分です。

解答はこのすぐ下の行です。文字の色を白にしてます。選択状態にすると見えます。
1217

2014年1月23日木曜日

プロジェクトオイラー 問89

プロジェクトオイラーの問題をpythonでやってみます。
日本語翻訳サイトは、プロジェクトオイラー日本語 でネット検索です。

問89「ローマ数字」
ローマ数字の記法は一つの数について沢山ある場合が存在する (FAQを見よ). 
しかし, ある数については最良の記法が必ず存在する.

例えば, 16の正しい記法を全て並べてみる.

IIIIIIIIIIIIIIII
VIIIIIIIIIII
VVIIIIII
XIIIIII
VVVI
XVI

最後の例は, 最小の文字数で表せるという意味で, 最も効率が良い.
11Kのテキストファイルroman.txt は1000個のローマ記法で書かれた数を含んでいる.
これらは, 正しい記法に従っている. 即ち, 大きい数から順に書かれていて, 
引き算ペアのルールにも従っている(このルールについてはFAQを見よ)
但し, 最小の文字数で表されているとは限らない.

最小形で書いたときに, 何文字節約できるかを求めよ.

注: ファイル中の全てのローマ数字には, 5つ以上の同じ文字が連続して含まれることはない.


FAQ: ローマ数字のルール

I = 1
V = 5
X = 10
L = 50
C = 100
D = 500
M = 1000

基本法則1
全ての文字は値の大きさの降順に並ぶ

基本法則2
引き算ペアについて.
例えば、4は5の1つ前なのでIVと表せる。
文字は値の大きさの降順に並べる必要があるので、
不適当に出てくる小さな値の文字は引くことを明確に示すとも言える。

X (10) + IX (9) として19=XIXと表せる. 
ただし, 8をIIXと二文字以上を引くことは許されない.

1. I, X, Cのみが引き算ペアの最初の文字として許される. 
2. IはVまたはXの前に来ることが許される 
3. XはLまたはCの前に来ることが許される 
4. CはDまたはMの前に来ることが許される






注意!!!
自力で解きたい人へ
以降の記述には解法に関するネタバレが含まれます。






私の回答例は以下の通りです。
def f(sIn):
	c = ["DCCCC CM","CCCC CD", "LXXXX XC", "XXXX XL", "VIIII IX", "IIII IV"]
	L = [s.split() for s in c]
	dif = 0
	pIn = open(sIn)
	for s in pIn.readlines():
		dif += len(s)
		for k in L: s = s.replace(k[0],k[1])
		dif -= len(s)
	pIn.close()
	pIn = None
	return dif
	
import os, sys
sIn = os.path.join(os.path.dirname(__file__), "problem089_roman.txt")
s = f(sIn)
print s


節約できる文字の組み合わせをリストに定義しておきます。
ファイルを1行ずつ読んでローマ数字を取得しながら、置換前の文字数は足してから節約した表記に置換し、置換後の文字数は引いて累計していきます。

解答はこのすぐ下の行です。文字の色を白にしてます。選択状態にすると見えます。
743

2014年1月18日土曜日

プロジェクトオイラー 問88

プロジェクトオイラーの問題をpythonでやってみます。
日本語翻訳サイトは、プロジェクトオイラー日本語 でネット検索です。

問88「積和数」
少なくとも2つの自然数 {a1, a2, ... , ak} の集合の和かつ積として表せる自然数Nを積和数と呼ぶ:N = a1 + a2 + ... + ak = a1 × a2 × ... × ak.
例えば, 6 = 1 + 2 + 3 = 1 × 2 × 3.

ある集合の大きさ k に対して,この性質を持つ最小の N を最小積和数と呼ぼう. 
集合の大きさ k = 2, 3, 4, 5, 6 に対する最小積和数は次のとおりである.
k=2: 4 = 2 × 2 = 2 + 2
k=3: 6 = 1 × 2 × 3 = 1 + 2 + 3
k=4: 8 = 1 × 1 × 2 × 4 = 1 + 1 + 2 + 4
k=5: 8 = 1 × 1 × 2 × 2 × 2 = 1 + 1 + 2 + 2 + 2
k=6: 12 = 1 × 1 × 1 × 1 × 2 × 6 = 1 + 1 + 1 + 1 + 2 + 6

したがって 2≦k≦6 に対して,全ての最小積和数の和は 4+6+8+12 = 30 である. 
8 は和に一度だけカウントされていることに気をつけよう.

実際, 2≦k≦12 に対する最小積和数の完全な集合は {4, 6, 8, 12, 15, 16} なので,その和は 61 である.

2≦k≦12000 に対する全ての最小積和数の和は何か?





注意!!!
自力で解きたい人へ
以降の記述には解法に関するネタバレが含まれます。






私の回答例は以下の通りです。
def f(N):
	L, M, maxn = [0]*(N+1), [2], int(1+(N-1)**(0.5))
	while M[0]<=maxn:
		n = reduce(lambda x,y:x*y, M)
		k = n-sum(M)+len(M)
		if 1<k<=N and not(0<L[k]<n): L[k]=n
		if k<=N: M.append(M[-1])
		else: M = M[:-2]+[M[-2]+1]
		
	L = set(L)
	return L, sum(L)
	
n = 12000
L, s = f(n)
print s


まず、例を手計算してもう少し進めてみました。
項目数k、最小積和数n、項目のリストMとして以下の通りです。
 k   n  M
 2:  4: 2,2
 3:  6: 1,2,3
 4:  8: 1,1,2,4
 5:  8: 1,1,2,2,2
 6: 12: 1,1,1,1,2,6
 7: 12: 1,1,1,1,1,3,4
 8: 12: 1,1,1,1,1,2,2,3
 9: 15: 1,1,1,1,1,1,1,3,5
10: 16: 1,1,1,1,1,1,1,1,4,4
11: 16: 1,1,1,1,1,1,1,1,2,2,4
12: 16: 1,1,1,1,1,1,1,1,2,2,2,2

和と積が同じとのことで項目に1がたくさんあるのは明らかです。
そこで、1と1以外に分けて、1以外の要素だけに注目します。
積和数は(1以外の要素積)です。
項目数は(1の要素数)+(1以外の要素数)です。
ここで、1の要素数は(積和数)-(1以外の要素和)なので、
項目数は(1以外の要素積)-(1以外の要素和)+(1以外の要素数)です。

次に、1以外の要素リストを昇順に並べてみます。
 k  M
 2: 2,2
 5: 2,2,2
12: 2,2,2,2
 8: 2,2,3
11: 2,2,4
 3: 2,3
 4: 2,4
 6: 2,6
 7: 3,4
 9: 3,5
10: 4,4
以上より、以下のルールで要素リスト候補を作り出します。
・項目数が項目数上限以内ならば、末尾の要素を新要素として追加する。
・項目数が項目数上限を超過したら、末尾要素を削除しておき、新末尾値を+1する。
 または最後から2個目の値を+1してここまでのリストにします。

上記の要素リスト候補に対して採用可否を判定することにします。

さらに問題文では要素数kの範囲が指定されますが、
要素リストをどこまで作成するかの限界を決めるために
項目数kのときの最小積和数の上限を求めておきます。
上限のとき、[1, 1, ..., 1, p, p]になります。1の個数は(k-2)個です。
このときの積和数は積=和なので、
p*p = k-2+p+p
p2 - 2p - (k-2) = 0
pの二次方程式として解いて、正の値をとると以下のとおりです。
p = 1+√(k-1)



1.関数f(N)
・項目数が2以上N以下の最小積和数の重複削除したリストとその和を求めます。

・L, M, maxn = [0]*(N+1), [2], int(1+(N-1)**(0.5))
 L:項目数ごとの最小積和数リスト
 M:1以外の項目リスト候補(要素は昇順)
 maxn:項目値の上限
 です。
 リストLは0クリアしておき最小積和数を設定していきます。
 
・while M[0]<=maxn:
 項目リスト候補Mの最初の値が項目値上限以内なら処理を続けます。

・n = reduce(lambda x,y:x*y, M)
 積和数nは、リストMの要素の積です。
 lambda関数で引数x,yを受け取り、その積x*yを返す無名関数を作り、
 reduce関数でリストMの要素を1つずつ累積的に上記関数に掛けていくことでリストMのすべての要素の積を求めます。

・k = n-sum(M)+len(M)
 項目数kは、(1以外の要素積-1以外の要素和=1の要素数)+1以外の要素数 です。

・if 1<k<=N and not(0<L[k]<n): L[k]=n
 採用可否の判定です。
 要素数が2以上N以下で、積和数リストLに新規設定または現値より小さければ設定します。

・if k<=N: M.append(M[-1])
 else: M = M[:-2]+[M[-2]+1]
 次の候補を作成します。
 項目数<項目数上限の場合、末尾の要素を追加します。
 項目数≧項目数上限値なら、末尾要素を削除しておき、新末尾値を+1する。
 つまり、最後から2個目の値を+1してここまでのリストとします。

・L = set(L)
 set関数でリストLの値をユニークにします。


解答はこのすぐ下の行です。文字の色を白にしてます。選択状態にすると見えます。
7587457

2013年11月17日日曜日

プロジェクトオイラー 問87

プロジェクトオイラーの問題をpythonでやってみます。
日本語翻訳サイトは、プロジェクトオイラー日本語 でネット検索です。

問87「3つの素数のべき乗」
素数の2乗と素数の3乗と素数の4乗の和で表される最小の数は28である. 
50未満のこのような数は丁度4つある.

28 = 22 + 23 + 24
33 = 32 + 23 + 24
49 = 52 + 23 + 24
47 = 22 + 33 + 24

では, 50,000,000未満の数で, 素数の2乗と素数の3乗と素数の4乗の和で表される数は何個あるか?





注意!!!
自力で解きたい人へ
以降の記述には解法に関するネタバレが含まれます。






私の回答例は以下の通りです。
def p(n):
	L = [0,0]+[1]*(n-1)
	for i in xrange(n):
		if i*i>n: break
		if not L[i]: continue
		for j in xrange(i+i, n+1, i): L[j] = 0
	return [i for i in xrange(n+1) if L[i]]
def f(n):
	P, L = p(int((n-2**3-2**4)**.5)), []
	for i in P:
		for j in P:
			for k in P:
				s = k**2+j**3+i**4
				if n<=s: break
				L.append(s)
	return len(set(L))

n = 50000000
m = f(n)
print m


1.関数p(n)
・n以下の素数リストを返します。問37を流用しました。

2.関数f(n)
・素数の2乗と素数の3乗と素数の4乗の和で表されるn未満の数の個数を返します。

・Pに素数、Lには素数の2乗と素数の3乗と素数の4乗の和を格納します。
 ただし、使用する最大の素数は、自身の2乗と2の3乗と2の4乗の和がn未満になる最大素数なので、nから2の3乗と2の4乗を差し引いた値の平方根の整数部分です。
 この使用可能な最大素数までの素数をリストPに格納します。

・素数リストLから、3重のfor文でi,j,kとして順番に1つずつ、重複を許して取り出し、それぞれを2乗、3乗、4乗して、その和を変数sとします。

・変数sがnを超えたら、それ以上のkのループは不要なのでkのfor文を終了します。
 j,kのループにも同様の判定を入れることは可能ですが、1分ルールをクリアしたのでこのままとします。
 

・return len(set(L))
 set文でリストLの要素の値をユニークにして、len関数で個数を数えて戻します。

解答はこのすぐ下の行です。文字の色を白にしてます。選択状態にすると見えます。
1097343