この数値は正確な-2の累乗ですか:(非常に)ハードモード


26

これは最近の課題のバージョンです。この数値は-2の整数乗ですか?問題の興味深い性質を強調し、課題をより困難にするために設計されたさまざまな基準を使用します。ここで考慮します

リンクされた質問でTobyが素晴らしく述べた挑戦は、次のとおりです。

整数が2の正確なべき乗であるかどうかを判断する賢い方法があります。それはもはや興味深い問題ではないので、与えられた整数が-2の正確なべき乗であるかどうかを判断しましょう。例えば:

-2 => yes: (-2)¹
-1 => no
0 => no
1 => yes: (-2)⁰
2 => no
3 => no
4 => yes: (-2)²

ルール:

  • 整数は、64ビット、符号付き、2の補数です。これは、使用できる唯一のデータ型です。
  • 次の操作のみを使用できます。これらはそれぞれ1つの操作としてカウントされます。
    • n << kn >> k:左/右シフトnによってkビット。符号ビットは右シフトで拡張されます。
    • n >>> k:右シフトしますが、符号ビットを拡張しません。0がシフトインされます。
    • a & ba | ba ^ b:ビット単位のAND、OR、XOR。
    • a + ba - ba * b:乗算、減算、追加。
    • ~b:ビット単位の反転。
    • -b:2の補数の否定。
    • a / ba % b除算(整数の商、0に向かって丸める)、およびモジュロ。
      • 負の数のモジュロはルール使用C99で指定する(a/b) * b + a%b等しいものとしますa。そう5 % -3です2、と-5 % 3あります-2
      • 5 / 3is 15 % 3is 2、as 1 * 3 + 2 = 5
      • -5 / 3is -1-5 % 3is -2、-1 * 3 + -2 = -5。
      • 5 / -3is -15 % -3is 2、-1 * -3 + 2 = 5
      • -5 / -3is 1-5 % -3is -2、1 * -3 + -2 = -5。
      • Pythonの//フロア除算演算子は、ここでは除算の「0に向かう丸め」プロパティを%満たさず、Pythonの演算子も要件を満たしていないことに注意してください。
    • 割り当ては操作としてカウントされません。Cの場合と同様に、代入は代入後の左側の値に評価されます:にa = (b = a + 5)設定さba + 5、次にに設定さab、1つの操作としてカウントされます。
    • 複合割り当ては、1つの操作としてa += b意味a = a + bおよびカウントに使用できます。
  • 整数定数を使用できますが、何もカウントされません。
  • 操作の順序を指定する括弧は許容されます。
  • 関数を宣言できます。関数宣言は、ユーザーにとって便利な任意のスタイルにすることができますが、有効なデータ型は64ビット整数のみであることに注意してください。関数宣言は操作としてカウントされませんが、関数呼び出しは1つとしてカウントされます。また、明確にするために、関数には複数のreturnステートメントを含めることができreturn、任意のポイントからのsが許可されます。return自身の操作としてカウントされません。
  • 変数を無料で宣言できます。
  • あなたは使用することができwhile、ループをしますが、使用することはできませんifforwhile条件で使用される演算子はスコアにカウントされます。whileループは、条件がゼロ以外の値に評価される限り実行されます(この概念を持つ言語の「真の」0は有効な結果ではありません)。早期返却が許可されているためbreak、使用も許可されています
  • オーバーフロー/アンダーフローは許可され、値のクランプは行われません。操作が実際に正しく行われ、64ビットに切り捨てられたかのように扱われます。

スコアリング/勝利基準:

入力が-2の累乗の場合、コードはゼロ以外の値を生成する必要があり、そうでない場合はゼロを生成する必要があります。

これはです。スコアは、実行時に実行される操作の総数ではなく、コードに存在する操作の総数(上記で定義)です。次のコード:

function example (a, b) {
    return a + ~b;
}

function ispowerofnegtwo (input) {
    y = example(input, 9);
    y = example(y, 42);
    y = example(y, 98);
    return y;
}

5つの操作が含まれます:関数内の2つ、および3つの関数呼び出し。

結果をどのように表示するか、言語で便利なものを使用するか、最終的に結果を変数に保存するか、関数から返すかなど、何でもかまいません。

勝者は、明らかに正しい(必要に応じてカジュアルまたは正式な証拠を提供する)投稿であり、上記のように最も低いスコアを持ちます。

ボーナス非常にハードモードチャレンジ!

パーティーで人々を感動させる潜在的な能力以外に絶対に勝つチャンスがないように、whileループを使用せずに答えを提出してください!これらが十分に提出された場合、勝者グループを2つのカテゴリ(ループありとなし)に分割することも検討できます。


注:32ビット整数のみをサポートする言語でソリューションを提供する場合は、説明で64ビット整数が正しいことを十分に正当化することを条件に、そうすることができます。

また、特定の言語固有の機能は、ルールを回避しないが、上記のルールに従って動作するように言語を強制する必要がある場合、無料で許可される場合があります。たとえば、(真理値)0を持つ言語の回避策として、条件全体に適用した場合、ループでの0以外の自由な比較を許可しwhileます。これらのタイプの事柄を利用しようとする明確な試みは許可されません。たとえば、「真の」0または「未定義」の値の概念は上記のルールセットに存在しないため、これらは当てにならない場合があります


コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
デニス

@hvdこれを読んだ場合:答えを完全に取り消すべきです!それも、正しいと仮定するとせず m ^= s、それはまだ印象的だ、と私はさらにそれを改善するための置換を作るために、完全にOKだろうと思います。
ジェイソンC

どのようにそれができるようにしても意味がないwhileし、breakではなくifif (x) { ... }はと同等while (x) { ... break; }です。
R ..

@R ..それは100%の意味をなさず(breakそして、早期のリターンは残念な部分です)、長い物語であり、将来の挑戦のためのルールで学んだ教訓です。常に「ボーナス」バージョンがあります!:)
ジェイソンC

1
なぜiffor禁止されていますか?int x=condition; while (x) { ... x=0; }無料で、さらに多くのコードがあります。c-styleでも同じですfor
-Qwertiy

回答:


35

C ++、15の操作

whileループがチャレンジ全体を破壊するため、ループが許可される理由はわかりません。以下に答えを示します:

int64_t is_negpow2(int64_t n) {
    int64_t neg = uint64_t(n) >> 63; // n >>> 63
    n = (n ^ -neg) + neg; // if (n < 0) n = -n;
    int64_t evenbits = n & int64_t(0xaaaaaaaaaaaaaaaaull >> neg);
    int64_t n1 = n - 1;
    int64_t pot = n & n1;
    int64_t r = pot | (n1 >> 63) | evenbits;
    return ~((r | -r) >> 63); // !r
}

whileループが課題全体を破壊するのはなぜですか?
ミスターXcoder

10
@ Mr.Xcoder課題は単純なビット単位の操作でそれを行うことでありwhile、あらゆる点でそれに反するためです。
-orlp

つまり、whileループに、静的nなどのループで実行された回数と操作の回数を掛けない限り、意味があります。
魔法のタコUr

ここでコメントしまし
ジェイソンC

@JasonCこれは、符号ビットのない右シフトを使用すべきだったからです。コードを編集しました(uint64_t符号拡張なしで正しいシフトを取得する唯一の方法だからです)
–orlp

25

Python 2、3の操作

def f(n):
 while n>>1:
  while n&1:return 0
  n=n/-2
 return n

オンラインでお試しください!

操作は>>&/

アイデアは繰り返し-2で除算することです。-2の累乗は1に連鎖します-8 -> 4 -> -2 -> 1。を押すと1、受け入れます。を1押す前に奇数をヒットした場合、拒否します。また0、を拒否する必要があります。

while n>>1:ループまで、n0または1ループ改であるn自体が返され、1Truthy出力と0 Falseyの一つ。ループ内で、繰り返し適用n -> n/-2を拒否し、奇数を拒否しますn

以来/しか偶数値に使用され、その丸め動作が場に出ることはありません。したがって、Pythonが仕様と異なる方法でラウンドすることは問題ではありません。


いいね アルゴリズムの巧妙なロジックと、条件をビット操作に組み合わせた優れた機能。また、確認することができ、実装はCで動作します
ジェイソンC

なぜwhile n&1代わりにif n&1
マークランサム

2
@MarkRansomチャレンジでは許可されていませんif
-xnor

ああ、それを見逃した。非常に巧妙な代替。
マークランサム

1
@EvSunWoodardスコアリングは、実行中の呼び出しの数ではなく、コード内の演算子の数であり、入力に依存します。「これはアトミックコードゴルフです。スコアは、コードに存在する操作の総数です。 」
-xnor

11

さび、 14 12の操作(ループなし)

最適化(-O)または-C overflow-checks=noパニックの代わりにオーバーフロー減算を有効にする必要があります。

fn is_power_of_negative_2(input: i64) -> i64 {
    let sign = input >> 63;
    // 1 op
    let abs_input = (input ^ sign) - sign;
    // 2 ops
    let bad_power_of_two = sign ^ -0x5555_5555_5555_5556; // == 0xaaaa_aaaa_aaaa_aaaa
    // 1 op
    let is_not_power_of_n2 = abs_input & ((abs_input - 1) | bad_power_of_two);
    // 3 ops 
    let is_not_power_of_n2 = (is_not_power_of_n2 | -is_not_power_of_n2) >> 63;
    // 3 ops 
    input & !is_not_power_of_n2
    // 2 ops
}

(明確にするために:ここで!xビット単位ではなく、論理的ではありません)

テストケース:

#[test]
fn test_is_power_of_negative_2() {
    let mut value = 1;
    for _ in 0 .. 64 {
        assert_ne!(0, is_power_of_negative_2(value), "wrong: {} should return nonzero", value);
        value *= -2;
    }
}

#[test]
fn test_not_power_of_negative_2() {
    for i in &[0, -1, 2, 3, -3, -4, 5, -5, 6, -6, 7, -7, 8, 1<<61, -1<<62, 2554790084739629493, -4676986601000636537] {
        assert_eq!(0, is_power_of_negative_2(*i), "wrong: {} should return zero", i);
    }
}

オンラインでお試しください!


アイデアは| x |かどうかをチェックすることです 2の累乗です((y & (y - 1)) == 0通常どおり使用)。xが2のべき乗であれば、我々はさらにチェック(1)ときx >= 0、それはまた、2、または(2)の偶数乗であるべきときx < 0、それは2の奇数力でなければなりません私たちは、ことによって、これを確認してください&"-ing bad_power_of_two"マスク0x…aaaaのときx >= 0(偶数のパワーのときのみ0を生成)、または0x…5555のときx < 0


~((r | -r) >> 63)は答えを修正するためにあなたのトリックを盗みました。
orlp

6

Haskell、2 3操作

import Data.Bits (.&.)

f 0 = False
f 1 = True
f n | n .&. 1 == 0 = f (n `div` -2)
f n | otherwise    = False

再帰関数を定義しますf(n)。使用される操作は、関数呼び出し(f)、除算(div)、ビット単位のand(.&.)です。

Haskellにはループステートメントがないため、ループは含まれていません:-)


4
ループを使用しないHaskellソリューションが「オポチュニスト」という名前のユーザーによって提供されていることに驚かないのはなぜですか。=)
コートアンモン-モニカの復活

1
アイム非常に躊躇f 0f 1f n ...ここで彼らは本質的であるためif、再度、私が許可しなかったが、変装してのwhile+ breakと早期returnそれは公正と思われるので、S。ルールセットが意図せずに解釈されないようにすることを利用しているように見えますが、それは良い解決策です。
ジェイソンC

3
特に、|sは特に空中にあります。と==はいえ、これは議論の余地のない方法で特定のルールに違反している:比較は許可されていません。このコードの私の解釈が正しいかどうか注意、しかし、ブール値の使用は、ここにいることはない結果を変更するには表示されません。その場所に任意の整数値を代入として許容現れ、彼らはより多くの、最終的なプレゼンテーション形式のです。
ジェイソンC

@JasonC Haskell ==からIntto Boolや "Truthy"にキャストする他の方法がないため、私は使用しています。パターンマッチングとガードが「no ifs」ルールに違反しているかどうかはあなたの電話です;
日和見主義者

18
パターンマッチングを使用すると、0の操作を使用して、すべての64ビット整数の結果をハードコードできます。
-xnor

5

Python 3、10、または11 9操作

def g(x):
 while x:
  while 1 - (1 + ~int(x - -2 * int(float(x) / -2))) & 1: x /= -2
  break
 while int(1-x):
     return 0
 return 5  # or any other value

戻り5の力のために-20そうでない場合


コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
デニス

5

C、5つの操作

long long f(long long x){
    x=x ^ ((x & 0xaaaaaaaaaaaaaaaa) * 6);
    while(x){
        while(x&(x-1))
            return 0;
        return 1;
    }
    return 0;
}

C、10演算、ループなし

long long f(long long x){
    x = x ^ ((x & 0xaaaaaaaaaaaaaaaa) * 6);
    long long t = x & (x-1);
    return (((t-1) & ~t) >> 63) * x;
}

C、1操作

long long f(long long x){
    long long a0=1, a1=-2, a2=4, a3=-8, a4=16, a5=-32, a6=64, a7=-128, a8=256, a9=-512, a10=1024, a11=-2048, a12=4096, a13=-8192, a14=16384, a15=-32768, a16=65536, a17=-131072, a18=262144, a19=-524288, a20=1048576, a21=-2097152, a22=4194304, a23=-8388608, a24=16777216, a25=-33554432, a26=67108864, a27=-134217728, a28=268435456, a29=-536870912, a30=1073741824, a31=-2147483648, a32=4294967296, a33=-8589934592, a34=17179869184, a35=-34359738368, a36=68719476736, a37=-137438953472, a38=274877906944, a39=-549755813888, a40=1099511627776, a41=-2199023255552, a42=4398046511104, a43=-8796093022208, a44=17592186044416, a45=-35184372088832, a46=70368744177664, a47=-140737488355328, a48=281474976710656, a49=-562949953421312, a50=1125899906842624, a51=-2251799813685248, a52=4503599627370496, a53=-9007199254740992, a54=18014398509481984, a55=-36028797018963968, a56=72057594037927936, a57=-144115188075855872, a58=288230376151711744, a59=-576460752303423488, a60=1152921504606846976, a61=-2305843009213693952, a62=4611686018427387904, a63=-9223372036854775807-1, a64=0;
    while(a0){
        long long t = x ^ a0;
        long long f = 1;
        while(t){
            f = 0;
            t = 0;
        }
        while(f)
            return 1;
        a0=a1; a1=a2; a2=a3; a3=a4; a4=a5; a5=a6; a6=a7; a7=a8; a8=a9; a9=a10; a10=a11; a11=a12; a12=a13; a13=a14; a14=a15; a15=a16; a16=a17; a17=a18; a18=a19; a19=a20; a20=a21; a21=a22; a22=a23; a23=a24; a24=a25; a25=a26; a26=a27; a27=a28; a28=a29; a29=a30; a30=a31; a31=a32; a32=a33; a33=a34; a34=a35; a35=a36; a36=a37; a37=a38; a38=a39; a39=a40; a40=a41; a41=a42; a42=a43; a43=a44; a44=a45; a45=a46; a46=a47; a47=a48; a48=a49; a49=a50; a50=a51; a51=a52; a52=a53; a53=a54; a54=a55; a55=a56; a56=a57; a57=a58; a58=a59; a59=a60; a60=a61; a61=a62; a62=a63; a63=a64;
    }
    return 0;
}

2
ああ、最後の1つはただの悪です。いいね
ジェイソンC

4

組み立て、1回の操作

.data

    .space 1         , 1 # (-2)^31
    .space 1610612735, 0
    .space 1         , 1 # (-2)^29
    .space 402653183 , 0
    .space 1         , 1 # (-2)^27
    .space 100663295 , 0
    .space 1         , 1 # (-2)^25
    .space 25165823  , 0
    .space 1         , 1 # (-2)^23
    .space 6291455   , 0
    .space 1         , 1 # (-2)^21
    .space 1572863   , 0
    .space 1         , 1 # (-2)^19
    .space 393215    , 0
    .space 1         , 1 # (-2)^17
    .space 98303     , 0
    .space 1         , 1 # (-2)^15
    .space 24575     , 0
    .space 1         , 1 # (-2)^13
    .space 6143      , 0
    .space 1         , 1 # (-2)^11
    .space 1535      , 0
    .space 1         , 1 # (-2)^9
    .space 383       , 0
    .space 1         , 1 # (-2)^7
    .space 95        , 0
    .space 1         , 1 # (-2)^5 = -32
    .space 23        , 0
    .space 1         , 1 # (-2)^3 = -8
    .space 5         , 0
    .space 1         , 1 # (-2)^1 = -2
    .space 1         , 0
dataZero:
    .space 1         , 0
    .space 1         , 1 # (-2)^0 = 1
    .space 2         , 0
    .space 1         , 1 # (-2)^2 = 4
    .space 11        , 0
    .space 1         , 1 # (-2)^4 = 16
    .space 47        , 0
    .space 1         , 1 # (-2)^6 = 64
    .space 191       , 0
    .space 1         , 1 # (-2)^8
    .space 767       , 0
    .space 1         , 1 # (-2)^10
    .space 3071      , 0
    .space 1         , 1 # (-2)^12
    .space 12287     , 0
    .space 1         , 1 # (-2)^14
    .space 49151     , 0
    .space 1         , 1 # (-2)^16
    .space 196607    , 0
    .space 1         , 1 # (-2)^18
    .space 786431    , 0
    .space 1         , 1 # (-2)^20
    .space 3145727   , 0
    .space 1         , 1 # (-2)^22
    .space 12582911  , 0
    .space 1         , 1 # (-2)^24
    .space 50331647  , 0
    .space 1         , 1 # (-2)^26
    .space 201326591 , 0
    .space 1         , 1 # (-2)^28
    .space 805306367 , 0
    .space 1         , 1 # (-2)^30
    .space 3221225471, 0
    .space 1         , 1 # (-2)^32

.globl isPowNeg2
isPowNeg2:
    movl dataZero(%edi), %eax
    ret

巨大なルックアップテーブルを使用して、数値が2の累乗であるかどうかを調べます。これを64ビットに拡張できますが、その量のデータを格納するコンピューターを見つけることは読者の課題です。


1
テーブルのインデックス作成は許可される操作の1つではありません。
R ..

1
また、これは明らかに64ビットに拡張できません。:-)
R ..

実際、テーブルのインデックス付けは、現在の規則の下で許可されることを意図していませんでした。スカラーの意図で「変数を宣言できます」と「整数リテラルを指定できます」を指定しましたが、意味的にこれは配列です(そして、厳密に言えば、配列型を許可せず、いずれかの種類のインデックスを操作は、アセンブラーのコンテキストでは「追加」と呼ぶことができますが)、あなたは日和見主義者であるということです... :)
ジェイソンC

3

C、31の操作

ライブデモ

私のアイデアはシンプルです。2のべき乗であれば、そのログが偶数であれば正でなければならず、そうでなければログは奇数でなければなりません。

int isPositive(int x) // 6
{
    return ((~x & (~x + 1)) >> 31) & 1;
}

int isPowerOfTwo(int x) // 5
{
    return isPositive(x) & ~(x & (x-1));
}

int log2(int x) // 3
{
    int i = (-1);

    while(isPositive(x))
    {
        i  += 1;
        x >>= 1;
    }

    return i;
}

int isPowerOfNegativeTwo(int x) // 17
{
    return (  isPositive(x) &  isPowerOfTwo(x) & ~(log2(x) % 2) )
         | ( ~isPositive(x) & isPowerOfTwo(-x) & (log2(-x) % 2) );
}

1
あなたは実際にあなたが思っているよりもうまくやった。関数呼び出しは、関数内の演算子の数としてではなく、1としてのみカウントされます。私が正しく(ダブルチェック)を数えてきたのであれば、あなたはisPowerOfNegativeTwo = 31のためのlog2 + 17のためのisPowerOfTwo + 3のためのDurationをisPositive + 5のための6のようなもの持っている
ジェイソン・C

1

C、7つの操作

int64_t is_power_of_neg2(int64_t n)
{
    int64_t x = n&-n;
    while (x^n) {
        while (x^-n)
            return 0;
        return x & 0xaaaaaaaaaaaaaaaa;
    }
    return x & 0x5555555555555555;
}

または:

C、条件付きループなしの13の演算

int64_t is_power_of_neg2(int64_t n)
{
    int64_t s = ~(n>>63);
    int64_t a = ((n/2)^s)-s;
    int64_t x = n&-(uint64_t)n; // Cast to define - on INT64_MIN.
    return ~(a/x >> 63) & x & (0xaaaaaaaaaaaaaaaa^s);
}

説明:

  • n&-nの最下位セットビットを生成しnます。
  • an/2負の絶対値で、必然的に負です/2オーバーフローを排除するです。
  • a/xa2の正確なべき乗である場合のみ、ゼロです。それ以外の場合、少なくとも1つの他のビットが設定され、それよりも高いx、最も低いビット、負の結果が生じます。
  • ~(a/x >> 63)次に、nまたは-n2の累乗である場合はすべて1、それ以外の場合はすべてゼロであるビットマスクを生成します。
  • ^sは、マスクに適用され、の符号をチェックして、nそれがの累乗かどうかを確認し-2ます。

1

PHP、3つの操作

三成分でありif、許可されていません。悪用しましょうwhile

function f($n)
{
    while ($n>>1)               # 1. ">>1"
    {
        while ($n&1)            # 2. "&1"
            return 0;
        return f($n/-2|0);      # 3. "/-2" ("|0" to turn it into integer division)
    }
    return $n;
}
  1. $n>>1:数値が0または1の場合、数値を返します
  2. $n&1:数値が奇数の場合、0を返します
  3. elseテスト$n/-2(+ intにキャスト)

0

JavaScript ES6、7オペレーション

x=>{
  while(x&1^1&x/x){
    x/=-2;x=x|0
  }
  while(x&0xfffffffe)x-=x
  return x
}

オンラインでお試しください!

説明

while(x&1^1&x/x)

一方、x!= 0およびx%2 == 0 4 ops
x / xは、xが0でない限り1に等しい(0/0はNaNを与え、これはfalseと評価される)
およびビット単位で、
x&1 ^ 1は1に等しいxが偶数(xおよび1)xor 1の場合

x/=-2;x=x|0

これは質問1 opで定義された除算の形式です

while(x&0xfffffffe)  

x!= 1およびx!= 0の場合 1 op
x == 0またはx == 1の場合に終了する必要がある条件は、これら2つが戻り値であり、無限ループに入ることは生産的ではありません。理論的には、16進数を増やすことで、より大きな値に拡張できます。現在、最大±2 ^ 32-1まで動作します

x-=x

xを0に設定1 op
に対してreturn 0を使用できましたが、別のステートメントによって中断されたwhileループは不正行為のように感じます。

return x

xを返します(-2のべき乗の場合は1、それ以外の場合は0)

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