有理数のビットごとのXOR


19

前書き

0から1までのすべての有理数は、最終的に周期的なビットシーケンスとして表すことができます。たとえば、11/40のバイナリ表現は

0.010 0011 0011 0011 ...

ここで、0011部品は無期限に繰り返されます。この表現を見つける1つの方法は次のとおりです。r = 11/40で開始し、それを繰り返し2倍して小数部分を取得し、1を超えたときに記録します。rの値が繰り返されると、ループに入ったことを確認できます。

1. r = 11/40
2. 2*r = 11/20 < 1   ->  next bit is 0, r = 11/20
3. 2*r = 11/10 >= 1  ->  next bit is 1, r = 2*r - 1 = 1/10
4. 2*r = 1/5 < 1     ->  next bit is 0, r = 1/5
5. 2*r = 2/5 < 1     ->  next bit is 0, r = 2/5
6. 2*r = 4/5 < 1     ->  next bit is 0, r = 4/5
7. 2*r = 8/5 >= 1    ->  next bit is 1, r = 2*r - 1 = 3/5
8. 2*r = 6/5 >= 1    ->  next bit is 1, r = 2*r - 1 = 1/5, same as in 4.
   The loop 5. -> 6. -> 7. -> 8. now repeats.

バイナリ文字列から11/40に戻すには、次の式を使用できます

(int(prefix) + int(suffix)/(2^len(suffix) - 1)) / 2^len(prefix)

どこprefixが最初の部分010suffixあり、繰り返し部分0011でありint、バイナリ文字列を整数に変換します。

このような表現が2つある場合、それらに対してビット単位のXOR演算を実行できます。結果のシーケンスも周期的であるため、有理数を表します。

一部の有理数には、2つのバイナリ表現があります。

1/4 = 0.010000000...
    = 0.001111111...

これらの選択は、ビット単位のXORの結果に影響を与える可能性があります。これらの場合、前者の表現を使用します。これには無限に多くの0があります。

タスク

入力は、半開区間[0,1)の2つの有理数です。出力は、入力に適用されるビットごとのXOR演算の結果であり、有理数として表されます。どちらの入力もそうでない場合でも、出力は1になる可能性があることに注意してください。

入力と出力の正確な形式は柔軟ですが、各有理数は分子と分母の2つの整数で表現する必要があります(0と1は例外として01必要に応じて表現できます)。入力は最低の用語で表現されると仮定できます。出力は最低の用語で表現する必要あります。組み込みの有理数型は、これらの制限を満たしている限り、受け入れ可能な形式です。言語によって課される整数の境界は無視できますが、アルゴリズムはすべての有理数に対して理論的に機能するはずです。

最も低いバイトカウントが優先されます。標準の規則が適用されます。

入力11/40および3/7を考慮してください。パイプで繰り返し部分を区切って、それらの表現を上下に記述し|ます。次に、同じ長さの繰り返し部分を抽出し、それらとその前の部分にビット単位のXORを適用します。

11/40 = 0. 0 1 0|0 0 1 1|0 0 1 1|0 0 1 1|0 0 1 1|0 0 1 1|0 0 1 1|0 0 1 ...
3/7   = 0.|0 1 1|0 1 1|0 1 1|0 1 1|0 1 1|0 1 1|0 1 1|0 1 1|0 1 1|0 1 1|...
     -> 0. 0 0 1|0 1 0 1 1 1 1 0 1 0 0 0|0 1 0 1 1 1 1 0 1 0 0 0|0 1 0 ...

結果の有理数は89/520です。

テストケース

0 0 -> 0
1/2 1/2 -> 0
1/2 1/4 -> 3/4
1/3 2/3 -> 1
1/2 3/4 -> 1/4
5/8 1/3 -> 23/24
1/3 1/5 -> 2/5
15/16 3/19 -> 257/304
15/16 257/304 -> 3/19
3/7 11/40 -> 89/520
5/32 17/24 -> 59/96
16/29 16/39 -> 621001733121535520/696556744961512799

サポートする必要がある最大期間は?
ニール

@Neilこのような最大値が存在すると思う理由は何ですか?
-orlp

3
注:一部の数値には2つのバイナリ展開があります。つまり、最終期間の長さが1である数値です。上記の問題定義で000...は、この場合に終わる表現を選択する必要があることは暗黙的です(アルゴリズムをで使用した場合に得られるものでもありますr)。例えば、場合に5/8, 1/3我々が得る23/24我々が拡大選ぶため0.101000...のために5/8。私たちが代わりに選択した場合0.10011111...のように5/8、XORがなった後の結果は19/24、これは間違っています。関連するウィキペディア:0.999 ...
ジャップスティグ・ニールセン

3
@JeppeStigNielsenくそ...これは、通常のXORと(a ^ b) ^ b == aは異なり保持されないことを意味します。例えば(19/24 ^ 1/3) ^ 1/3 != 19/24。そのため、このことについてかなり興奮しています。(
orlp

回答:


3

Python 3、193 164バイト

def x(a,b,z=0):
 l=[]
 while(a,b)not in l:l+=[(a,b)];z=2*z|(a<.5)^(b<.5);a=a*2%1;b=b*2%1
 p=l.index((a,b));P=len(l)-p
 return((z>>P)+z%2**P*a**0/~-2**(P or 1))/2**p

入力をPython 3のfractions.Fractionタイプとして受け取り、同様に出力します。

おもしろい事実(生成関数を使用してこれを簡単に表示できます)。上記に変更(a<.5)^(b<.5)する((a>=.5)and(b>=.5))と、2つの有理数の間で2進数ANDが得られます。これを呼び出しますnd(a, b)。その後、私たちは持っていa + b - 2*nd(a, b) = x(a, b)ます!


確かに、私の間違い。謝罪!(答えに含まれるtioへのリンクが素晴らしいことに注意してください)
Xcoder氏

1

JavaScript、141バイト

(p,q,r,s)=>(h=(v,u)=>v%u?h(u,v%u):[a/u,b/u])(b=(g=x=>x%q||x%s?g((x|x/2)+x%2):x)(1),a=(o=b/(b-(b&~-b)),x=p*b/q,y=r*b/s,(x%o^y%o)+(x/o^y/o)*o))

最後のテストケースで問題が発生しました(整数オーバーフロー)。に4つの数値を入力しp/q xor r/s、2つの数値を持つ配列を出力します。テストケースの0, 0場合は、入力する必要があります0, 1, 0, 1

どうやって:

(ここで説明する数値はすべてバイナリ形式です。)

  1. 最小の番号を見つけbb = 10 ^ p - 10 ^ q (p, q are integers, p > q); and b = 0 (mod q); and b = 0 (mod s);
  2. ましょうx = p * b / qy = r * b / q; 変換p / qr / sx / by / b
  3. させるo = 10 ^ (p - q) - 1; スプリットxy[x % o, x / o][y % o, y / o]。各部分のxorを取得し[x % o xor y % o, x / o xor y / o]、再び結合し(x % o xor y % o) + (x / o xor y / o) * oます。として寄付してくださいa
  4. の場合a = 0、答えは0(または0 / 1)です。そうでなければ聞かせてu = gcd(a, b)。答えはある(a/u)(b/u)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.