CodeIQで結城先生が出題されているということで、久しぶりに挑戦してみました。せっかくブログを始めたので、その解答を晒しておきたいと思います。
【問題】
今回の問題、サルベジオン問題は、データベースからWebAPIを用いてあるキーに対応するバリューを取得するというものです。ただし、厄介なことにデータベースが故障しているため、キーを入力してバリューを得るということができません。その代わりに、データのインデクスを入力することで、そのインデクスに対応するキーとバリューのペアを得ることができます。この条件で、あるキーに対応するバリューを取得せよというのが問題です。
なおデータベースはdb1とdb2の2つがあり、それぞれ1つのキーに対応するバリューを求めます。また、データベースへのアクセスは1秒に1回だけという制限があります。
【解法】
db1は最初の100個ぐらいを出力してみて、キーが単調増加っぽいなーと思ったので、とりあえず二分探索してみると、目的のキーのインデクスを求めることができました。
db2も最初の100個ぐらいを出力してみて、キーの規則性を探しました。ここで、データの個数がに等しいということがわかりました。なんとなく眺めていると、いくつか単調増加したあとに小さくなり、また単調増加して、というのが繰り返されているように見え、また単調増加の長さがだんだんと長くなっているということに気が付きました。
そこで、単調増加の始点のインデクスを見てみると、すべて2のべき乗だったので、2のべき乗ごとに何らかの規則で単調増加しているのだろうと思いました。そこから、差分をとったり、インデクスが2のべき乗のキーを眺めてみたり、なんやかんやすると、
という規則があることに気が付きました。ここではインデクスがであるキーを表します。よってインデクスを求めたいキーをとすると、
を満たすを見つければ良いことになります。なので私はを0から100まで動かした時のを上の式から求め、インデクスがのデータをひとつひとつ調べました。
以下がそのコード(Python)です。コード中では上で出てきたをN、をKとしています。
db1
import urllib2 import time def getData(db, index): response = urllib2.urlopen('http://salvageon.textfile.org/?db=%d&index=%d' % (db, index)) list = response.read().split(' ') key = int(list[2].translate(None, 'K')) value = int(list[3].translate(None, 'V')) return [key, value] K = 208050656559285601386927895421059705239114932023754 lb = 0 ub = 1267650600228229401496703205376 while ub - lb > 1: mid = (ub + lb) / 2 print '%d, %d' % (lb, ub) data = getData(1, mid) if data[0] == K: print "index:%d, value:%d" % (mid, data[1]) break if data[0] > K: ub = mid if data[0] < K: lb = mid time.sleep(1)
import urllib2 import time def getData(db, index): response = urllib2.urlopen('http://salvageon.textfile.org/?db=%d&index=%d' % (db, index)) list = response.read().split(' ') key = int(list[2].translate(None, 'K')) value = int(list[3].translate(None, 'V')) return [key, value] N = 633825300114114700748351602688 K = 2023636070998557444542586045 for n in range(101): m = ((2**n * K) / N - 1) / 2 if m < 0 or m >= 2**n: continue index = 2**n + m data = getData(2, index) print "%d, %d, %d" % (n, m, index) if data[0] == K: print "index:%d, value:%d" % (index, data[1]) break time.sleep(1)