分岐しないでください


14

低レベルのコード最適化にある程度慣れている人は、if-statements、loops、またはselect-statementsとして実装されても、分岐の予測ミスの可能性はひどいクロックを浪費するものです。

単純な問題は単純な算術ではるかによく解決できるので、そうしましょう。

次の問題では、すべての変数は32ビット符号なし整数であり、許可されるコードは次の演算子のみを含むプレーンセットステートメントのみです。

+ addition
- subtraction
* multiplication
/ integer division, rounds down, division by 0 not allowed
% modulo
& binary and
| binary or
^ binary exclusive or
>> bitshift right
<< bitshift left

Logic operators, return 1 if the expression is true and 0 if it is false.
== equal
!= not equal
< less than
<= less than or equal
> greater than
>= greater than or equal

Set operator
=

すべての行は、変数識別子と、それに続く集合演算子と、それに続く式で構成する必要があります。

式には追加のセット演算子を含めることはできませんが、変数識別子、リテラル番号、括弧を含めることができます。

ゴルフのスコアは、オペレーターの数のみを数えるものとします。

例:

myvar = ( ( ( foo + 5 ) * bar ) % 7 ) == 3

5つの演算子のスコアがあります。

ソリューションには、作成者が適切と考える変数をすべて含めることができます。
設定されていない変数にはvalueがあります0
オーバーフローとアンダーフローは許可され、すべての負の数はアンダーフロー3 - 5です。4294967294としても、より大きな文の一部として、。

タスク1:最大

2つの値、AおよびBがスコープ内に存在しRESULT、プログラムの終了時に変数に最大値が含まれるようにします。

タスク2:中央値

3つの値が、ABおよびC、作る、スコープ内に存在しますRESULTときに、プログラムが終了変数は、それらの値の中央値が含まれています。

タスク3:平方根

1つの値Aがスコープに存在し、RESULT変数に次の平方根が含まれるようにしますA、プログラムの終了時に切り捨てられます。

質問の1つまたは2つだけに答えを投稿しても構いません。有効な解決策を見つけるだけの人もいるでしょう。


単項演算子はどこにありますか?私は気にしない-けど~(私は何のためかわからない場合でも)いいかもしれません。
ジョン・ドヴォルザーク

確かに、0xFFFF_FFFF_FFFF_FFFF ^ xそして0 - x。どうして忘れたの?
ジョンドボラック

@JanDvorak最短の説明を作成しました。完全性のロジック!はかなり簡単ではないためです:x == 0
aaaaaaaaaaaa

ゼロ除算の動作は何ですか?
ジョン・ドヴォルザーク

Mathematicaでは(a> b)TrueまたはFalseを返します。BooleはFalseを0に、Trueを1に変換します。使用することは合法Boole[a-b]ですか?
DavidC

回答:


5

タスク3、23操作

x = (A >> 16) + A / ((A >> 13) + 511) + 15
x = (x + A/x) >> 1
x = (x + A/x) >> 1
x = (x + A/x) >> 1
RESULT = x - (x > A/x)

他のソリューションと同様に、より巧みに選択されたシードを使用して、ニュートンの方法を使用します。最初のビットA >> 16は範囲の上部を幸せにA / ((A >> 13) + 511)保ち、2番目のビットは範囲の中央を幸せに保ち、最後のビット15はゼロエラーによる除算を防止します(15は0正しく収束できる最大値です-半分3回のマイナス補正はゼロに等しい)。入力値225, 275625, 82137969, 2908768489(および近くの値)の場合、初期シードは正確です。範囲のすべてのエッジケース(完全な正方形、完全な正方形+ 1、および完全な正方形-1)0 .. 2**32-1がテストされ、正しいです。

ルールに関するコメント:
オーバーフローとアンダーフローが許可され、すべての負の数がアンダーフローになるため、3-5 は大きな文の一部であっても 4294967294 です。

その最後のビットは、イノベーションのキラーのようなものであることが判明しました。私は最初に一般化された形式のハレー法を使用して解決策を試みましたが、上記の制限を考慮すると無効であることに気付きました。反復(平方根に適用される)は次のとおりです。

x = x * (3*A + x*x) / (A + 3*x*x)

この反復には、ニュートンにはない優れた性質があります。(二次ではなく)立方体に収束し、上からだけではなく上または下から収束し、適切に選択されていないシードにはそれほど敏感ではありません(ニュートンの反復に提供されるシードが低すぎると、収束点を大幅にオーバーシュートした後、元に戻す必要があります)。

ニュートンの方法には、(少なくとも整数を扱う場合)A / x-x = 2のようなxに達することが多いという問題もあります。この場合、適切な整数のルートよりも1大きい値に収束します。修正が必要です。ハレーの方法はそのような修正を必要としません。しかし、残念ながら、の値は、多くの場合、許可されている32ビット整数スペースよりも大きくなります。3*A + x*x

他の一般化されたnがいくつかあります番目のルートアルゴリズムがそれらはすべてこの同じ特性を共有しています。

x = x + x*(v - x**n)/(v*n)
x = (x*(n+1) - x**(n+1)/v)/n
x = ((n-2)*x + (4*v*x)/(v + x**n))/n
x = x*((n+2)*v + (n-2)*x**n)/(v + x**n)/n
x = ((n-2)*x + (n*x*v)/(v + (n-1)*x**n))/(n-1)
x = ((n-2)*x + x*((n*2-1)*v + x**n)/(v + (n*2-1)*x**n))/(n-1)

x = x + 2*x*(v - x**n)/(v + x**n)/n
x = x + x*31*(v - x**n)/(10*v + 21*x**n)/n
x = x + x*561*(v - x**n)/(181*v + 380*x**n)/n
x = x + x*1153*(v - x**n)/(372*v + 781*x**n)/n

など。これらのほとんどは、3次または2次収束のいずれかを表示します。最後の4つは、4次収束に収束する一連の反復の一部です。しかし、実際には、何百もの桁を計算する必要がない限り、Newtonの方法は少ない操作で必要なものを取得します。


かなりいいですが、4294967295で失敗します。ルールに関しては、面白くするためにきつくする必要があります。どの施設が最善の課題であるかを議論することはできますが、最終的には、ルールが正確に許容するものよりも、ルールが明確で明確であることの方がはるかに重要です。
aaaaaaaaaaaa

とにかくハレーはそれだけの価値があるとは思いません。遠く離れた推測から、3倍より少しだけ改善されると思いますが、ニュートンは2倍より少し小さくなります。ニュートンは精度を倍にします。そのため、ハレーの1回の反復は、log(3)/log(2) ~= 1.585ニュートンの反復とまったく同じ価値があります。
aaaaaaaaaaaa

@eBusiness私は最初、2つのハレーを持ち、同様に選択された合計25オペレーションのシードを使用しましたA = 0。約4294967295、それは見落としでした:65536²≡0として、修正の繰り返しは修正に失敗します。別の方法が見つかるかどうかを確認します。
primo

@eBusinessが修正されました。
primo

パックのなめらかな平方根、素晴らしい仕事、そして公式の勝利バッジ。
aaaaaaaaaaaa

5

65(61)操作(5 + 13 + 47(43))

タスク1 -Max(A、B)

RESULT = A + (B - A) * (A <= B)

これは明らかな解決策です。割り当てが必要で、比較が必要です。比較に何かを掛ける必要があります。被乗数は変数の1つにはならず、積は結果にはなりません。

タスク2-中間(A、B、C)

RESULT = A                               \
       + (B - A) * (A > B) ^ (B <= C)    \
       + (C - A) * (A > C) ^ (C <  B)

これは、3つの変数すべてを条件付けた以前の15-opソリューションに対する改善です。これにより、2つの減算が節約されましたが、別の中心性テストが導入されました。テスト自体は単純です:要素が真ん中にあるのは、2つのうちの1つが上にある場合です。

タスク3 -sqrt(A)

X1     = 1024 + A / 2048
X2     = (X1  + A / X1 ) / 2
...
X10    = (X9 + A / X9 ) / 2
RESULT = X16 - (X16 * X16 > A)

ニュートン近似の11ラウンド。1024という魔法の定数は既に打ち負かされています WolframWます(512はa = 2 ** 32が収束する前にa = 0でゼロで除算します)が、0/0をゼロとして定義できる場合、開始値で10回の反復が機能します512の繰り返し。10回の繰り返しという私の主張は完全にクリーンではないことは認めますが、それでも括弧内に主張しています。 ただし、9が可能かどうかを調査する必要があります。WolframHのソリューション 9回の反復です。


タスク3の最初の行は正しくないと思います。2番目の定数は最初の定数の4倍でなければなりません(「純粋な」ニュートンを得るため)。
モニカを

@WolframHより良い初期推測は、なぜサイクルを無駄にしているのかを説明するかもしれません。どこで4 *を思いつきましたか?これは、2つの反復が1つにロールバックされたように見えます。
ジョンドヴォルザーク

(1024 + A/1024)/2 == (512 + A/2048)(これはのようなものX0 = 1024で、その後Newtonを起動します)。
モニカを

タスク1の素敵なソリューション。コロンバスの卵。
DavidC

@DavidCarraherもちろん、正しい解は次のようになりますMOV RESULT, A; CMP A,B; CMOVA RESULT, B;-)
ジョン・ドヴォルザーク

5

1:5人のオペレーター

RESULT = B ^ (A ^ B)*(A > B)

2:13人のオペレーター

RESULT = B ^ (A ^ B)*(A > B) ^ (A ^ C)*(A > C) ^ (B ^ C)*(B > C)

3:27人のオペレーター

g = 47|((0x00ffffff & A)>>10)|(A>>14)
r = (g + A/g)/3
r = (r + A/r)>>1
r = (r + A/r)>>1
r = (r + A/r)>>1
RESULT = r - (r*r-1>=A)

5

タスク3、39操作

編集:最後の行を変更しました。コメントを参照してください。

これは、Newthonメソッドの実装です。すべての正の二乗、および正の二乗から1を引いたもの、および0から2 ^ 32-1の範囲の100万個の乱数でテストされました。一見おかしいと思われる開始値は、の短縮であり(1022 + A/1022) / 2、反復回数が最も少なく(私は思う)、またRESULTfor A=0権利(これはの1024代わりではない1022)になります。

r = (511 + A/2044)
r = (r + A/r) / 2
r = (r + A/r) / 2
r = (r + A/r) / 2
r = (r + A/r) / 2
r = (r + A/r) / 2
r = (r + A/r) / 2
r = (r + A/r) / 2
r = (r + A/r) / 2
RESULT = r - (r > A/r)

あなたの方法と並行して最適化され、かなりの時間後に投稿されたニュートンの方法の劣ったコピーを保持する必要がありますか?偉大な人は同じように考え、解決策を2つの2つの答えに分割するのは悪いことですが、それが現状です。#2には答えていないからです。
ジョンドヴォルザーク

@JanDvorak:お問い合わせいただきありがとうございます。私の少し短い方法を答えに入れても大丈夫です。また、私にクレジットを与えてくれてありがとう:
モニカを復活

本当にいい試みですが、入力4294965360〜4294967295で失敗します。–
aaaaaaaaaaaa

@eBusiness:これらの入力に対してどのような結果が得られますか?テストで65535を取得しましたが、問題ありません。
モニカ

65536を取得します。規定の整数形式を使用していない可能性があります。
aaaaaaaaaaaa
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.