最終更新: 2011-03-02T05:24+0900
何も考えずにコーディングしただけ。一瞬 CPUが考え込みます。
generators = [
lambda{ n = 0
lambda{ n+=1; n*(n+1)/2 }
}.call,
lambda{ n = 0
lambda{ n+=1; n*n }
}.call,
lambda{ n = 0
lambda{ n+=1; n*(3*n-1)/2 }
}.call,
lambda{ n = 0
lambda{ n+=1; n*(2*n-1) }
}.call,
lambda{ n = 0
lambda{ n+=1; n*(5*n-3)/2 }
}.call,
lambda{ n = 0
lambda{ n+=1; n*(3*n-2) }
}.call,
]
# 数を準備
d4polynumbers = generators.map{|g|
() while (p = g.call) < 1000
a = [p]
a.push(p) while (p = g.call) < 10000
a
}
# 端緒(の集まり)
bunch_of_chain = d4polynumbers[d4polynumbers.size-1].map{|p|
[[p, d4polynumbers.size-1]]
}
# 端緒を伸ばすもの
extender = lambda{|chain, pool|
xx = chain.last.first.to_s[-2,2]
( (0...(pool.size)).to_a - chain.map{|_| _.last } ).map{|i|
[i, pool[i]]
}.map{|i, nums|
nums.find_all{|num|
num.to_s[0,2] == xx
}.map{|num|
chain + [[num, i]]
}
}.inject(&:+)
}
# 伸ばしていく
(d4polynumbers.size-1).times{
bunch_of_chain = bunch_of_chain.map{|chain|
extender[chain, d4polynumbers]
}.inject(&:+)
}
# 輪っか?
bunch_of_cyclic_chain = bunch_of_chain.reject{|chain|
chain.first.first.to_s[0,2] != chain.last.first.to_s[-2,2]
}
# 出力
bunch_of_cyclic_chain.each{|chain|
puts chain.map{|a,_| a }.join("\t")
puts chain.map{|_,b| "P#{b+3}" }.join("\t")
puts "sum: #{chain.map{|a,_| a }.inject(&:+)}"
}
先は長いのにもう失速してる。「良いもの。悪いもの。: Project Eulerを100問解いてみた」テトレーションとか聞いたこともない単語なんだけど……。
中学生の時に 3^{50} の一の位は何かという問題が出た。でも Problem 188は何乗したらいいかもわからない。下手の考え休むに似たりっていうけどどうしたもんかなあ。ない知恵を絞るのも悪くないと思うんだけど。
最終更新: 2011-03-04T22:27+0900
認証結果(GET/POSTデータ)の再利用によるなりすましを防ぐために nonceを使うのが一般的。でもそうするとコメントのプレビュー時にはまだ認証ができないことになる。事前に認証してしまったら自分自身がその結果を再利用するかたちになるから。tDiaryはアカウントやログインって概念を管理してないから OpenIDによる認証結果をそれらと結びつけて持続させることができない。認証とコメントの投稿が同時のぶっつけ本番。書き込み前にどういう表示になるのかはやっぱり知りたいよ。
Bloggerの解。
表示名には、OpenID プロバイダから Google に送信されたあなたの名前が使用されます。表示名がない場合は、OpenID の URL から表示名の取得を試みます。
こういう割り切りが必要なのかな。でも Yahoo!!!なんかは味気ない URLしか返してこないよね(※1)。mixiしか聞かない、表示名が取得できたってのは。もちろん、myOpenIDもユーザーフレンドリーな名前を返す。ペルソナをかぶることすらできるから選んだ。OpenID Providerとしての Googleはひと味違って claimed_idが realmごとに固有のものに変化するらしい(※2)。Webサービスからすると、ユーザーをリダイレクトしたときと返ってきたときの claimed_idが変わったように見える。OpenIDをいろんなサービスへのログイン手段としてだけみるなら、余分な情報を渡さないのはメリット。自己紹介として URLを提示したときにはそれと違うものが claimed_idとして Webアプリに渡るのはデメリット。さて、独自ドメインの URLを提示しておいて、そのドメインから Googleへ認証を delegateした場合の claimed_idはどうなる?
※1 知らぬ間に AX(Attribute Exchange)に対応してた。「Yahoo! JAPAN、OpenIDでプロフィール情報を提供 拡張仕様「AX」「UI」に対応:CodeZine」でも、SREGには対応しないってどういうこと? AXの汎用性は却って対応が面倒なんだけど。「Attribute Exchange のメモ - Yet Another Hackadelic」定義済みのシェーマがあるらしいが、すでに二つもある。
※2 もっとも、そのことを知ったのはこういうタイトルの記事。「OpenIDでGoogleからグローバルユニークなユーザー識別子を取得できるかもしれない方法 - r-weblife」realm(Webサービスのドメイン)固有の claimed_id ↔ 「グローバルユニークなユーザー識別子」
閑話休題。nonceをワンタイムでなく時限式にするしかないのだろうか。タイムアウトって嫌いなんだけど。
OpenID Providerへコメントの全文ごとユーザーをリダイレクトするのはまずいかなと思って、通常通りコメントを保存した後でリダイレクトし、正しく認証されて返ってきたときに、その印としてコメントに OP名を付与することを考えた。問題はその認証がどのコメントに対するものなのかだ。ユーザーを送り出すときに「このパラメータと一緒に返ってきてね」ということはできるが……。コメントを他者が推測できない UUIDみたいなものとともに保存しておいてユーザーにそれを運んでもらおうか。(もちろんどの日の日記に対するコメントなのかを表すパラメータもユーザーに運んでもらう)
最終更新: 2011-03-12T03:46+0900
"exactly five" って書いてあるから、同じ数字の並べ替えで作れる立方数が 6以上あってもダメだと思うんだ。
memo = Hash.new{|h,k| h[k] = [] }
n = 0
k_length = 1
loop{
n += 1
cube = n*n*n
k = cube.to_s.split(//).sort.join('')
if k.length != k_length
answer = memo.values.select{|cubes| cubes.size == 5 }
if not answer.empty?
answer.each{|cubes| p cubes }
exit
end
memo.clear
k_length = k.length
end
memo[k] << cube
}
# (x-1)/x <= log10(n) < 1 (n = ?,?,...)
count = 0
x = 0
loop{
x += 1
boundary = (x-1.0)/x
lower_bound = (1..9).to_a.reverse.find{|n| Math.log10(n) < boundary } || 0
count += 9 - lower_bound
break if lower_bound == 9
}
p count
またまた「Dreamshire | Project Euler Problem 63 Solution」の解答を検討してみたい。二重のループなんてない。logの計算だって 9回だけ。どういうことだ?
というストーリーをひねり出した。「9は 10より 0.046(=1-0.954)程度小さい数だ」ってくだりがいかにも苦しい。小数だからごまかしがきいてるけど、ぴったり 10一個分小さくなる場合は n桁、n-1桁、どっち? (たぶんまだ n桁だな。1^1がそう)
ともあれ、明かされてみればワンライナーの問題だったよ。
p (1..9).inject(0){|sum,n| sum + (1/(1-Math.log10(n))).floor }
常用対数を直接求めるメソッドが用意されてるあたりが Rubyだなとおもた。
連分数っていうらしい。a_nの求め方、a += 1 while 0 <= r - (n - d*(a+1))**2 の条件部分が判然としない。スクリプト中のコメントにあるように、対象としてるルートの係数が必ず約分されて 1になることも理解できてない。
def next_frac(r, n, d) # (√r + n) / d = a + 1 / [(√r + n_) / d_]
a = 0
a += 1 while 0 <= r - (n - d*(a+1))**2
d_ = (r - (n - d*a)**2) / d
raise if (r - (n - d*a)**2) % d != 0 # why OK?
n_ = -(n - d*a)
return a, r, n_, d_
end
def period_of(r)
rnd = [r, 0, 1]
arr = []
loop{
a, *rnd = next_frac(*rnd)
arr << rnd
period = arr.size-1 - arr.index(rnd)
return period if 0 != period
return 0 if rnd[2] == 0 # √r is rational.
}
end
count = 0
1.upto(10000){|n|
count += 1 if period_of(n) % 2 == 1
}
p count
ところで、この問題を解くときに Math.sqrtを使うのってインチキくさくない?(だから使ってないんだけど)
最終更新: 2011-03-12T02:24+0900
分母を一番深いところから順番に計算していく。
a = ([2] + (1..33).map{|k| [1,2*k,1] }.inject(&:+)).reverse
denom, numer = *a.inject([0,1]){|nd, x| [nd[1], x*nd[1]+nd[0]] }
require 'rational'
p Rational(numer, denom).numerator.to_s.chars.inject(0){|sum,c| sum - ?0 + c[0] }
xを増やしながらの総当たりで、最後に見つかった Dが答え。と思ったんだけど Dが見つかるペースがどんどこ落ちていく。一日以上かけても 969個の Dのうち 270個が残ってる。
Problem 18の延長で以前解いた。
前問に引き続いて、数学でもプログラミングでもなく、オラクルで。
最終更新: 2011-03-15T00:15+0900
「HCF(n,d)=1」には用語の説明があると思ったんだけどなかった。n/dの nと dは最大公約数が 1の既約分数だとすると意味がとおるので HCF=Highest Common Factorだと決めた(でっちあげ)。
分数とはなんぞやだとか切断だとか小難しく考えてしまったが(実際には考えられるほど知らない)、ワンライナーだった。3/7より少し小さい100万個の分数を小数になおして、一番小さいものを見つける。有理数にして比較しないのは時間がかかるから。公約数をみつけたりする時間だろうか。
require 'rational'
p Rational(*(2..1_000_000).inject([0,1]){|answer,d|
answer[0]/answer[1].to_f < (d*3-1)/7/d.to_f ? [(d*3-1)/7,d] : answer
})
Project Euler Problem #71 « KeyZero Conversation
分数を初めてならった小学生が必ず間違える分数の足し算(通分せずに分母どうし分子どうしを加算する)にこんな意味があるとか!
分単位のお時間がかかります。(訳:一時間はかからないけど……)
何倍も速くなるので「Integer#prime_division」を使う代わりに 100万要素の配列を使ってる。トレードオフで使用メモリは数MBから 100MB超になるが。 小手先のチューンよりアルゴリズムを改良しろってのはもっともだけど、かなしいかな、できることとできないことがあるのです。
LIMIT = 1_000_000
pfs = Array.new(LIMIT+1){ [] }
count = 0
2.upto(LIMIT){|d| # 0) LIMIT以下のすべての分母 d に対して、
print d,"\r"
count += d-1 # 1) とりあえず d-1 通りの分子を計上し、
d.step(LIMIT, d){|_| pfs[_] << d } if pfs[d].empty?
# 2) 8分の6など通分可能なものを差し引きする。
(1..(pfs[d].size)).each{|r|
cms = pfs[d].combination(r).map{|pf| pf.inject(&:*) }
count -= (-1)**(r%2+1) * cms.map{|cm| (d-1)/cm }.inject(&:+)
}
}
p count
Ruby 1.9からバックポートされてきた(のだと思われる見覚えのないメソッド) cycle, tap, combination, permutation, productといったメソッドが便利だ。あとは自然数を無限に生成し続ける無限リストのようなものをどれだけ簡単に書けるかだ。なにかショートカットがあるのだろうか。これでは長すぎる。
Enumerable::Enumerator.new(lambda{|&block| n=0; loop{ block.call n+=1 } }, :call).each{|x| p x }
それと、block.callの部分を yieldにできないのもわかりにくい。Procと blockと lambdaの微妙な違いによるものなのだろうか。
Rubyによる他所の Project Eulerの解答をみていてこういう書き方も知ってるけど、カウンタが Floatになっちゃうのが不満。
1.upto(1/0.0){|n| p n }
あれ? Fixnumだ。Floatになるのは stepだった。
1.step(1/0.0){|n| p n } # 1.0, 2.0, 3.0,...
明示的に Fixnumの増分: 1を指定しても n は Float. この違いはなんだろう。
cycleの使い道として zipを想定していたが拒否されてしまった。
irb> [1,2,3,4,5].zip([0]) => 1, 0], [2, nil], [3, nil], [4, nil], [5, nil irb> [1,2,3,4,5].zip([0].cycle) TypeError: can't convert Enumerable::Enumerator into Array from (irb):2:in `zip' from (irb):2 from :0 irb> RUBY_DESCRIPTION => "ruby 1.8.7 (2010-01-10 patchlevel 249) [i386-mswin32]"
最終更新: 2011-03-14T23:14+0900
昨日(20110312p01)の延長。Problem 72の解答と同じ部分より違う部分を見つける方が難しい。
LIMIT = 12000
pfs = Array.new(LIMIT+1){ [] }
count = 0
2.upto(LIMIT){|d| # 0) LIMIT以下のすべての分母 d に対して、
print d,"\r"
d.step(LIMIT, d){|_| pfs[_] << d } if pfs[d].empty?
n_min, n_max = d/3, (d-1)/2 # (n_min, n_max]
next unless n_min < n_max
count += n_max - n_min # 1) とりあえず一通りの分子を計上し、
# 2) 8分の6など通分可能なものを差し引きする。
(1..(pfs[d].size)).each{|r|
cms = pfs[d].combination(r).map{|pf| pf.inject(&:*) }
count -= (-1)**(r%2+1) * cms.map{|cm| n_max/cm - n_min/cm }.inject(&:+)
}
}
p count
問題文のヒントを最大限に利用したが数分かかる。総当たりでなく組み合わせ単位でテストしてその順列を計上したらマシになるかも。順列を考えるときに先頭の桁に 0を置くようなミスを犯しそうだがね。
factorial = [1,1] # [0!,1!,...]
factorial.push(factorial.size*factorial.last) until 9 < factorial.size
chain_length = lambda{
memo = {
169 => 3, 363601 => 3, 1454 => 3,
871 => 2, 45361 => 2,
872 => 2, 45362 => 2
}
f = lambda{|start|
return memo[start] if memo.has_key?(start)
next_ = start.to_s.chars.map{|c| factorial[c[0]-?0] }.inject(&:+)
return memo[start] = 1 + (start == next_ ? 0 : f.call(next_))
}
}.call
p (1...1_000_000).inject(0){|sum,n| sum += 1 if chain_length.call(n) == 60; sum }
最終更新: 2011-03-14T21:52+0900
♪ 前後の衛星写真をくらべて言葉を失った。地形がかわっとる。町がない。思い出せばリポーターは最初からこの状況を伝えようとしていた。そして、言葉からでは想像が難しかったことが衛星写真で明らかになったように、衛星写真でわかることは実際の万分の一もない。
1号機に続いて 3号機でも(建物内の水素ガスが)爆発。2号機の冷却機能喪失。原発設置のハードルがさらに上がってしまったのが残念。非現実的なコストを積み上げて想定される災害対策を行っても、心情的に無理なんじゃないか。
専門家は癌を死の病だと盲目的におそれる患者(キャスターがその役)に対するように、不安を煽らないように煽らないようにしゃべっていた。でもそれでは不信を招く。これだけ浴びたらこういう症状が出ます、という情報も知りたい。当事者には汚染の有無だけが重要で、汚染はあるんだし、避難勧告に従っていれば大丈夫っていっても、避難できなかった人が被曝して「「除染」後の検査でも高い放射線量の値を示したため、第2次被曝医療機関に搬送されていた」んだけど。
不勉強。
最終更新: 2012-02-29T17:34+0900
前のバージョンはここ(20110312p01.02)。step2を最適化*すると倍くらい速くなって 1分半(ウチのPC基準で。C++だとコンマ1秒)。それに伴い素因数の組み合わせを求める必要がなくなったので、100万要素の配列の配列が 100万要素の Fixnum配列になってメモリ使用量が再び数MBレベルになった。
LIMIT = 1_000_000
pfs = Array.new(LIMIT+1, 1)
count = LIMIT*(LIMIT+1)/2 - 1 # 1) 2からLIMIT以下の分母 d につき d 通りの分子を予め計上する。
2.upto(LIMIT){|d| # 0) 2からLIMIT以下の分母 d に対して、
print d,"\r"
d.step(LIMIT, d){|_| pfs[_] *= -d } if pfs[d] == 1
# 2) 分子が d で分母が d の倍数になるものを加減する。
count += pfs[d]/pfs[d].abs * (LIMIT/d)*(LIMIT/d+1)/2 if pfs[d].abs == d
}
p count
一週間後にはこのコードが理解できなくなってること請けあい。
ググった>「Dreamshire | Project Euler Problem 72 Solution」。φ関数の値を合計するとかで、Rubyでも10秒未満。Problem 69に出てきた関数だけど、その問題はインチキしたから理解してないんよね。
LIMIT = 1000000
phi = (0..LIMIT).to_a
2.upto(LIMIT){|n|
n.step(LIMIT, n){|m|
phi[m] *= (n-1.0)/n
} if phi[n] == n
}
p phi.inject(&:+).floor-1
繰り返しの構造は似てるのにこの実行時間の差はなんだ? と思ったら print d,"\r" の有無だけだった。俺のも同等に速い。
* 全体でみると同じ数字を足したり引いたりを繰り返してる気がしたので個々の dに特有の値だけを加減するように。
最終更新: 2011-03-21T02:43+0900
Problem 31と同じ問題。その解答(20110125p01.03)。ただし、Problem 31には通用した俺の解法では全然終わらない。
Target = 100
a = [1] + [0]*Target
(Target-1).downto(1){|pick|
0.upto(Target-pick){|i|
a[i+pick] += a[i]
}
}
p a[Target]
素数ごとに一から再試行してるけど計算量はたかがしれてた。
require 'mathn'
prime_gen = Prime.new
primes = []
prime_gen.each{|prime|
primes.push prime
a = [1] + [0]*prime
primes.each{|pr|
0.upto(prime-pr){|i|
a[i+pr] += a[i]
}
}
answer = a.index{|x| 5000 < x }
if answer
p answer
exit
end
}
まったくひどい解答。76から続くシリーズの締めらしく、何も考えないと計算量が膨大になる。手続き的な解法から一歩進む必要がある。それか一分ルールを無視して何時間もかけて良しとするか。
Target = 100000
a = [1] + [0]*Target
(Target-1).downto(1){|pick|
0.upto(Target-pick){|i|
a[i+pick] += a[i]
}
}
p a.index{|x| x%1_000_000 == 0}
最終更新: 2014-04-25T14:53+0900
迷路より簡単。右下から左上に向かって、右の要素と下の要素を参照しながら順番に処理するだけ。
matrix = DATA.lines.map{|ln| ln.chomp.split(",").map(&:to_i) }
raise "正方行列でない!" if matrix.size != matrix[0].size
(matrix.size-1).downto(0){|i|
(matrix[i].size-1).downto(0){|j|
incr = nil
incr = matrix[i][j+1] if j+1 < matrix[i].size
incr = matrix[i+1][j] if i+1 < matrix.size && (!incr || matrix[i+1][j] < incr)
matrix[i][j] += incr if incr
}
}
p matrix[0][0]
__END__
content of matrix.txt here.
まだまだ簡単。最下段から、行を右へ左へ処理しながら上へ向かうだけ。こういう、問題・入力に依存して可変長のメモリを確保したりしない、そのうえ問題を単純に走査するだけの解法は安心できる。
Matrix = DATA.lines.map{|ln| ln.chomp.split(",").map(&:to_i) }.transpose # transpose:問いの右から左が、下から上への処理になる。
Order = Matrix.size
raise "正方行列でない!" if Matrix.size != Matrix[0].size
row = Matrix[0].dup # 1-line memo. row is now at the first(top) line of Matrix.
1.upto(Order-1){|i|
# move from up ↓↓↓↓↓↓↓↓↓↓
0.upto(Order-1){|j|
row[j] += Matrix[i][j]
}
next if i == Order-1 # 最後の行は横移動不要(※禁止ではない)。最小値だけを選び取って答えにするから。
# move right →→→→→→→→→→
0.upto(Order-2){|j|
src, dst, move_cost = row[j], row[j+1], Matrix[i][j+1]
row[j+1] = src + move_cost if src + move_cost < dst
}
# move left ←←←←←←←←←←
(Order-1).downto(1){|j|
src, dst, move_cost = row[j], row[j-1], Matrix[i][j-1]
row[j-1] = src + move_cost if src + move_cost < dst
}
}
p row.min
__END__
content of matrix.txt here.
Array#transposeを使う機会があるなんて思わなかった!好きなメソッドは transpose(今日だけ)。ま、使わなくてもいいんだけど線形にアクセスするために。ま、メモリ構造からは遠く離れた Rubyなんだけど。
N×N確保していた作業メモを 1行分だけで済ませるようにスクリプトを修正。
コメントに「transpose:問いの右から左が、下から上への処理になる。」ってあるけど、今問題文を見ると左から右になってる。まあ、どっちからどっちでも変わらないからね。問題文が左から右になったからってわけではないけど、アップデート後は上から下の処理に変えてる。下から上だと、どうしてもその必然性を探してしまうから。
シリーズの締め。迷路のときとは違って 80×80ともなると手当たりしだいに探索の手を伸ばしていくと 10分以上の時間がかかる。優先度を付けると insertのコストが加わったにもかかわらず、笑っちゃうぐらい一瞬で終わった。
C++だったら queueの実装として std::multimapを使うところだけど配列をヒープ構造にするのもありだ。
Matrix = DATA.lines.map{|ln| ln.chomp.split(",").map(&:to_i).freeze }.freeze
raise "正方行列でない!" if Matrix.size != Matrix[0].size
matrix = Matrix.map{|ln| Array.new(ln.size) }
size = matrix.size
moved = lambda{|i,j, l,m|
return false if not (0...size).include?(l) or not (0...size).include?(m)
src, dst, move_cost = matrix[i][j], matrix[l][m], Matrix[l][m]
return false if dst && dst < src + move_cost
matrix[l][m] = src + move_cost
return true
}
matrix[0][0] = Matrix[0][0]
queue = [[0,0]]
insert = lambda{|l,m|
val = matrix[l][m]
queue.insert(queue.index{|i,j| val <= matrix[i][j] }||queue.size, [l,m])
}
until queue.empty?
i,j = *(queue.shift)
break if matrix.last.last and matrix.last.last <= matrix[i][j]
[[i-1,j],[i+1,j],[i, j-1],[i,j+1]].each{|l,m|
if moved[i,j, l,m]
insert[l,m]
end
}
end
p matrix.last.last
__END__
content of matrix.txt here.
<queue>ヘッダには priority_queueクラスがあるし、<algorithm>には make_heap, pop_heap, push_heap といった、配列(RandomAccessIteratorをそなえたコンテナ)にかぶせて使うための関数があった。そりゃあるわなあ。ソートキーが要素のみから算出できない今回の場合に priority_queueを使う(外部キー)か、multimapを使う(内部キー)かはやっぱり決めかねるけど。
最終更新: 2011-08-20T02:12+0900
incorrect
Target = 2_000_000
Size = Math.sqrt(Target*2).floor+1
a = (1..Size).map{|i| i*(i+1)/2 }
answer = 0
until a.empty?
jv = a.last
answer = [a.map{|iv| iv*jv }.min_by{|v| (v-Target).abs }, answer].min_by{|v| (v-Target).abs }
a.pop
end
p answer
まったく恥ずかしい。答えが合わないとなって当然問題を読み直してはいたんだけど、日を置いて改めて読んでみたら問題が何を求めてるのかが見えてきた。"nearest solution" ではなく "area" だったとさ。
Target = 2_000_000
Size = Math.sqrt(Target*2).floor+1
a = (1..Size).to_a
answer = [0,0]
until a.empty?
j = a.last
answer = ([answer] + a.map{|i| [i,j] }).min_by{|i,j| (i*(i+1)/2*j*(j+1)/2 - Target).abs }
a.pop
end
puts "#{answer[0]} * #{answer[1]} = #{answer[0]*answer[1]}"