分岐予測の課題


8

毎日、毎分、...マイクロ秒ごとに、多くの決定がコンピュータによって行われます。高水準言語では、これらは通常ifwhileおよびのようなステートメントの形式をとりforますが、最も基本的なレベルでは、分岐/ジャンプ命令と呼ばれる機械語命令が存在します。現代のプロセッサは、内の命令キューに入れパイプラインプロセッサはすぐにブランチ(それがされていない、つまり、次の命令でパイプラインを埋めるためにかどうかを決定する必要があること、そしてこの手段とら)または分岐先の命令の。

プロセッサが正しく推測しない場合、誤ってパイプラインに入力された命令を無視し、正しい命令をフェッチする必要があるため、遅延が発生します。分岐予測子の仕事は、パイプラインを再充填するというコストのかかるアクションを回避するために分岐が行われるかどうかを試行錯誤することです。

過去の決定のシーケンスを前提として、次の決定を正しく推測する予測子を作成する必要があります。プログラムが不明瞭/ゴルフ言語である場合、そのインタープリターへのリンクを指定すれば、プログラムは任意の言語で作成できます。最初のコマンドライン引数として過去の実際の履歴を取る必要がありますが、シーケンスの最初の推測には提供されません。次に、それをstdoutに出力して、推測を返す必要があります。決定は「y」または「n」の形式です。各テストケースは72の決定のシーケンスであるため、指定した履歴引数が71文字を超えることはないと想定できます。たとえば、「Alternating 1」テスト:

ynynynynynynynynynynynynynynynynynynynynynynynynynynynynynynynynynynynyn

プログラムが次の場合は失格となります。

  • 1秒以内に結果を返さない
  • yまたはを返しませんn(新しい行は問題ではなく無視されます)。
  • 他の候補者のコードを含む、このチャレンジに関連するコードまたはファイルを変更しようとする試み
  • 他に悪意のあるものが含まれています

必要に応じて、永続化のためにファイルを使用できますが、一意の名前を付け、上記に準拠する必要があります。

これはであり、ではありません。勝利は、ブランチプレディクタープレディクターによって、ソリューションがテストスイート全体で最も多くのブランチを正常に予測した候補に与えられます。テスト:

yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy,All yes
nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn,All no
ynynynynynynynynynynynynynynynynynynynynynynynynynynynynynynynynynynynyn,Alternating 1
nnyynnyynnyynnyynnyynnyynnyynnyynnyynnyynnyynnyynnyynnyynnyynnyynnyynnyy,Alternating 2
yyynnnyyynnnyyynnnyyynnnyyynnnyyynnnyyynnnyyynnnyyynnnyyynnnyyynnnyyynnn,Alternating 3
nnnnyyyynnnnyyyynnnnyyyynnnnyyyynnnnyyyynnnnyyyynnnnyyyynnnnyyyynnnnyyyy,Alternating 4
yyyyyynnnnnnyyyyyynnnnnnyyyyyynnnnnnyyyyyynnnnnnyyyyyynnnnnnyyyyyynnnnnn,Alternating 5
nnnnnnnnnnnnyyyyyyyyyyyynnnnnnnnnnnnyyyyyyyyyyyynnnnnnnnnnnnyyyyyyyyyyyy,Alternating 6
yyyyyyyyyyyyyyyyyynnnnnnnnnnnnnnnnnnyyyyyyyyyyyyyyyyyynnnnnnnnnnnnnnnnnn,Alternating 7
yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyynnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn,Alternating 8
yynyynyynyynyynyynyynyynyynyynyynyynyynyynyynyynyynyynyynyynyynyynyynyyn,2-1
ynnnynnnynnnynnnynnnynnnynnnynnnynnnynnnynnnynnnynnnynnnynnnynnnynnnynnn,1-3
nyyyyynyyyyynyyyyynyyyyynyyyyynyyyyynyyyyynyyyyynyyyyynyyyyynyyyyynyyyyy,5-1
nnnnnnnnnnnynnnnnnnnnnnynnnnnnnnnnnynnnnnnnnnnnynnnnnnnnnnnynnnnnnnnnnny,1-11
nyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyynyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy,35-1
yynnnnyynnnnyynnnnyynnnnyynnnnyynnnnyynnnnyynnnnyynnnnyynnnnyynnnnyynnnn,2-4
ynnyyynyynnnynnyyynyynnnynnyyynyynnnynnyyynyynnnynnyyynyynnnynnyyynyynnn,1-2-3
ynynynynynynynynynynynynynynynynynynyyyyyyyyyyyyyyyyyynnnnnnnnnnnnnnnnnn,A1/A7
yyyyyynnnnnnyyyyyynnnnnnyyyyyynnnnnnnnyynnyynnyynnyynnyynnyynnyynnyynnyy,A5/A2
nnnnnnnnnnnnyyyyyyyyyyyynnnnnnnnnnnnyyyynnnnyyyynnnnyyyynnnnyyyynnnnyyyy,A6/A4
nyyyyynyyyyynyyyyynyyyyynyyyyynyyyyynyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy,5-1/35-1
yyynnnyyynnnyyynnnnyyyyyyyyyyyyyyyyyyyyyyynyyyyynyyyyyyynnnnyynnnnyynnnn,A3/Y/5-1/2-4
yynnnnyynnnnyynnnnnyynnnynnyyynyynnnnnnynnnynnnynnnynnyynyynyynyynyynyyn,2-4/1-2-3/1-3/2-1

テストとランナープログラムの完全なセットは、このGitHubリポジトリにあります。予測子をsrc/predictors.txtフォームに追加して<name>,<command to run>実行するだけmain.pyです。単純なテストのために、2つの非常に基本的な予測子をすでに提供しています。ランナーを実行するときは、出力をきれいにするために、少なくとも92の列幅をお勧めします。バグを見つけた場合はお知らせください。:)


3
私の意見では、ブランチプレディクターの本質を捉えていないため、この課題に反対しました。現在の課題は、分岐予測子ではなく、一般的な予測AIについてです。少なくともブランチプレディクタの課題には、非常に厳しいメモリ要件と増分履歴が必要であり、すべての決定はメモリアドレスの関数でなければなりません。また、あなたの挑戦は自己完結型ではありません。
orlp

4
私はこの挑戦が好きです。純粋に50/50のランダムなテストケースは悪い考えだと思います。ただし、80/20のランダム、およびランダムなパーツを含む他のテストケース(現在の一部のテストケースのように)は問題ありません。すべてのテストケースを投稿に含めることをお勧めします。
Nathan Merrill

@NathanMerrillおかげで、私はテストからすべてのランダム性を削除しました。
Doddy

1
@Doddy申し訳ありませんが、私はコイン当てゲームを意味しました。1人のプレイヤーが0と1を生成し続け、もう1人が推測しようとします。
orlp

2
これらのテストケースの最適化を避けるべきですか?誰かが履歴を既知のテストコーパスと比較し、95%以上のスコアを
付ける

回答:


14

コンプレッサー(ルビー)、スコア1502/1656≈90.7%

require 'zlib'
input = $*[0].to_s.chomp
recent_input = input[-35..-1] || input
puts recent_input.chars.uniq.min_by { |char| [Zlib::Deflate.deflate(input+char,9).size,-input.count(char),-input[-10..-1].to_s.count(char)] } || ?y

最後に「y」または「n」が追加された場合、現在の文字列がより圧縮可能かどうかを確認します。同等に圧縮できる場合は、最も多く表示されるものを使用します。


7

デジャブ(Mathematica)、スコア503/552≈91.123%

If[Length[$ScriptCommandLine] < 2, Print["y"]; Exit[]];
input = Characters[$ScriptCommandLine[[2]]] /. {"y" -> 1, "n" -> 0};
For[test = Length[input], ! 
   ListQ[ker = FindLinearRecurrence[input[[-test ;;]]]], test--];
Print[LinearRecurrence[ker, input[[-test ;; Length[ker] - test - 1]], 
     test + 1][[-1]] /. {1 -> "y", _ -> "n"}];

パターンの線形反復を探し、次の項を計算します。テスト用に、として保存しDéjàVu,MathematicaScript -script <file>ます。


2

歴史家(Kotlin)、スコア1548/1656≈93.478%

過去から未来を予測します。

fun main(args : Array<String>) {
    if (args.size == 0) {
        println('y')
        return
    }
    val history = args[0].reversed()
    var bestLength = 0
    var ys = 0
    var ns = 0
    for (i in 1..history.length-1) {
        var length = 0
        var j = i
        while (j < history.length) {
            if (history[j] == history[j - i]) {
                length++
            } else {
                break
            }
            j++
        }
        if (length > bestLength) {
            bestLength = length
            ys = 0
            ns = 0
        }
        if (length == bestLength) {
            if (history[i - 1] == 'y') {
                ys++
            } else {
                ns++
            }
        }
    }
    println(if (bestLength == 0) history[0] else if (ys >= ns) 'y' else 'n')
}

コンパイル:kotlinc Historian.kt
実行:kotlin HistorianKt


1

パターンファインダー(Java)、スコア1570/1656(≈94.8%)1532/1656(≈92.5%)

複雑なパターンのわずかな改善。

public class Main {

    public static void main(String[] args) {
        if (args.length == 0) {
            System.out.print("y");
            return;
        }
        System.out.print(new Pattern(args[0]).getNext());
    }

}

class Pattern {

    private String pattern;
    private int index;
    private int length;

    public Pattern(String string) {
        setPattern(string);
    }

    private void setPattern(String string) {
        if (string.length() == 0) {
            this.pattern = "y";
            this.index = 0;
            this.length = 1;
            return;
        }
        if (string.length() == 1) {
            this.pattern = string;
            this.index = 0;
            this.length = 1;
            return;
        }
        if (string.startsWith("ynnyyy")) {
            this.pattern = "ynnyyynyynnn";
            this.index = string.length() % 12;
            this.length = 12;
            return;
        }
        this.length = 0;
        char first = string.charAt(0);
        char current = first;
        boolean hasReverse = false;
        while (length < string.length() - 1 && !(hasReverse
                && current == first)) {
            hasReverse |= current != first;
            length++;
            current = string.charAt(length);
        }
        if (!(hasReverse && current == first)) {
            this.pattern = Character.toString(current);
            this.index = 0;
            this.length = 1;
            return;
        }
        this.pattern = string.substring(0, length);
        int i = length;
        while (i <= string.length() - length) {
            if (!string.substring(i, i + length).equals(pattern)) {
                setPattern(string.substring(i));
                return;
            }
            i += length;
        }
        this.index = string.length() % i;
        if (!string.substring(i).equals(pattern.substring(0, index))) {
            setPattern(string.substring(i));
        }
    }

    public char getNext() {
        char result = pattern.charAt(index);
        index = (index + 1) % length;
        return result;
    }

}

@TheNumberOneよろしいですか?もしそうなら、私はそれをスコアとして入れます。投稿は順調です。あなたは私に勝ちます!
TheCoffeeCup 2016年

ああ、1-2-3テストケースのパフォーマンスが向上しました。
TheNumberOne 2016年

@TheNumberOneはい、いくつかの深刻なテストの後、私はそれが私の割合を殺していることに気づきました。質問は、私が許可されなかったことを正確に述べていません...
TheCoffeeCup

1

Factorio(Python3)、スコア1563/1656≈94.38%

シーケンスを左から右に一連の繰り返しパターンと交互パターンに分解します。主に長い一致長を優先しますが、タイの場合は、交互のパターンでは繰り返しを選択し、長いサイクル長では短くすることを選択します。

def main():
    if len(sys.argv) < 2:
        print('y')
        sys.stdout.flush()
        return
    history = sys.argv[1]
    while history:
        score = 0
        period = 0
        l = 0
        for p in range(1, len(history)):
            if history[0] == history[p]:
                m = lambda a, b: a == b
                s0 = 1
            else:
                m = lambda a, b: a != b
                s0 = 0
            s = 0
            for i in range(len(history)):
                j = i + p
                if j < len(history):
                    if not m(history[i], history[j]):
                        break
                    s += 1
            if s > score or s == score and s0 > score0:
                score = s
                score0 = s0
                period = p
                match = m
                l = j
        if period == 0:
            print(history[0])
            sys.stdout.flush()
            return
        if l >= len(history):
            break
        l = (l // period) * period
        history = history[l:]
    print('y' if match(history[-period], 'y') else 'n')
    sys.stdout.flush()

if __name__ == '__main__':
    main()

0

リピーター(Python)、スコア1173/1656≈70.83%

この予測子は、履歴がない場合は単にyesと推測し、それ以外の場合は以前の実際の結果を繰り返します。

import sys

if len(sys.argv) == 1:
   print "y"
else:
   print sys.argv[1][-1:]

!リピーター(Python)、スコア483/1656≈29.17%

履歴がない場合、この予測子はノーと推測します。そうでない場合、最後の実際の結果の反対を繰り返します。

import sys

if len(sys.argv) == 1:
   print "n"
else:
   if sys.argv[1][-1:] == "y":
      print "n"
   else:
      print "y"

2タッチ(Python)、スコア1087/1656≈65.64%

以前に同じ結果が2つ以上ある場合、またはこれまでに1つの結果があった場合、この予測子は同じ結果を選択します。履歴がない場合は「y」を選択します。少なくとも2つの結果があり、最新の2つが同じではない場合、最新の結果とは逆の結果が選択されます。

import sys

if len(sys.argv) == 1:
   print "y"
elif len(sys.argv[1]) == 1:
   print sys.argv[1][-1:]
else:
   l = len(sys.argv[1])

   if sys.argv[1][l - 2:l - 1] == sys.argv[1][-1:]:
      print sys.argv[1][-1:]
   else:
      if sys.argv[1][-1:] == "y":
         print "n"
      else:
         print "y"

0

私はコメントを残しますが、50担当者の要件は私を妨げます。

@histocratの回答を少し改善できました

コンプレッサー(ルビー)、スコア1504/1656≈90.82%

require 'zlib'
input = $*[0].to_s.chomp
recent_input = input[-35..-1] || input
puts recent_input.chars.uniq.min_by { |char| [Zlib::Deflate.deflate(input+char,9).size,-input[-3..-1].to_s.count(char),-input.count(char)] } || ?y

私はさまざまな方法で微調整しましたが、+ 0.12%の改善が最高でした。

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