この「三人の子の年齢当てパズル」に絡めて、
【純粋な論理パズルです】A「私の3人 の子供の年齢を当ててみて」B「ヒントは?」A「年齢の積は36」B「もう少しヒントを」A「年齢の和はあなたの年齢と同じ」B「、、、じゃあ1つ質問。 年齢が一番上の子の名前は?」A「たかゆき」B「OK、それで分かった」 さて、3人の子供の年齢は?
Twitter / 池田洋介
掛け合わせると36になる3つの数の組を列挙する(つまり、36を3つの因数に分解する)機能が欲しくなったのですが、簡潔に書けないかとつぶやいたところ、@nishioさんがこんな RT をくれたので、
[(x,y,z)for x in range(37)for y in range(37)for z in range(37)if x*y*z==36] RT @sumim かけると36になる三つの数の組み合わせをすべて列挙するコードを140文字以下で書きたいが書けない。
Twitter / nishio hirokazu
内包表現までは無理としても、Squeak4.1 にはジェネレーターがあったような…と書いてみたのがこちら。
| gen triples | gen := Generator on: [:g | (1 to: 36) asDigitsToPower: 3 do: [:digits | (digits inject: 1 into: #*) = 36 ifTrue: [g value: digits copy]]]. triples := Set new. gen do: [:triple | triples add: triple sort]. ^triples
=> a Set( #(1 1 36) #(1 6 6) #(1 2 18) #(3 3 4) #(2 3 6) #(1 3 12) #(1 4 9) #(2 2 9))
Python のジェネレーターや内包表現のようにスマートではないのですが、まあ、この手の処理を書くときには便利に使えそうです。
で、件のパズルのほうは(以下はネタバレ注意)、これら因数の組をその和でグループ分けしてみれば謎は解けるわけです。Squeak Smalltalk であれば、#groupBy:having: を使うのがいいでしょう。
| gen triples | gen := Generator on: [:g | (1 to: 36) asDigitsToPower: 3 do: [:digits | (digits inject: 1 into: #*) = 36 ifTrue: [g value: digits copy]]]. triples := Set new. gen do: [:triple | triples add: triple sort]. ^triples groupBy: [:tri | tri sum] having: [:grp | grp size > 1]
"=> a PluggableDictionary(13->an OrderedCollection(#(1 6 6) #(2 2 9)) ) "
Python で書いてつぶやいたのはこちら。
@nishio なるほど。def splitnum3(n): return set([tuple(sorted([x,y,z])) for x in range(n+1) for y in range(n+1) for z in range(n+1) if x*y*z==n])
Twitter / sumim
@nishio 完成。 [(k, map(lambda x : x[1:], list(vs))) for k, vs in groupby(sorted([(sum(t),) + t for t in splitnum3(36)]), key=lambda x : x[0])]
Twitter / sumim
これに対する @nishoさんの手直し版がこちら。
@sumim r=range(37);[(k,[x[1]for x in vs])for k,vs in groupby([(x+y+z,(x,y,z))for x in r for y in r for z in r if x*y*z==36],lambda x:x[0])]
Twitter / nishio hirokazu