昔ながらのナイスな方法でこれをハッキングしようとしている人は誰もいないのでreduce
、私はこの仕事を引き受けます。このメソッドは、引数の配列に対して繰り返されるアクションのループを実行し、デフォルトではこのループを中断する方法がないため、このような問題には柔軟ではありません。interupted reduce
次のように独自の for interrupted ループを実装すると、ドアが開きます。
from functools import reduce
def inner_func(func, cond, x, y):
res = func(x, y)
if not cond(res):
raise StopIteration(x, y)
return res
def ireducewhile(func, cond, iterable):
# generates intermediary results of args while reducing
iterable = iter(iterable)
x = next(iterable)
yield x
for y in iterable:
try:
x = inner_func(func, cond, x, y)
except StopIteration:
break
yield x
その後、標準の Python reduce メソッドfunc
の入力と同じものを使用できるようになります。これを次のように定義します。func
def division(c):
num, start = c
for i in range(start, int(num**0.5)+1):
if num % i == 0:
return (num//i, i)
return None
数値 600851475143 を因数分解したいと仮定すると、この関数を繰り返し使用した後のこの関数の期待される出力は次のようになります。
(600851475143, 2) -> (8462696833 -> 71), (10086647 -> 839), (6857, 1471) -> None
タプルの最初の項目は、division
メソッドが取り、2 番目の項目から始まり、この数値の平方根で終わる最小の除数で割ろうとする数値です。除数が存在しない場合は、None が返されます。次に、次のように定義されたイテレータから始める必要があります。
def gener(prime):
# returns and infinite generator (600851475143, 2), 0, 0, 0...
yield (prime, 2)
while True:
yield 0
最後に、ループの結果は次のとおりです。
result = list(ireducewhile(lambda x,y: div(x), lambda x: x is not None, iterable=gen(600851475143)))
#result: [(600851475143, 2), (8462696833, 71), (10086647, 839), (6857, 1471)]
また、素因数の出力は次のようにキャプチャできます。
if len(result) == 1: output = result[0][0]
else: output = list(map(lambda x: x[1], result[1:]))+[result[-1][0]]
#output: [2, 71, 839, 1471]
ノート:
より効率的にするために、この範囲のすべての値ではなく、特定の範囲にある事前生成された素数を使用することができます。