6

この質問は以前に尋ねられましたが、どれも明確に答えられていなかったので、ここで見つけたすべての情報をまとめてみました. 必要に応じて、自由に別の stackexchange サイトにマージ/移動してください。

これに関連して私が見つけた質問は次のとおりです。

この問題は当初、Interviewstreet Code Sprint として投稿されましたが、現在は練習問題として掲載されています。SPOJ にも移植されています。

問題文は次のとおりです。

N枚のカードをシャッフルするアルゴリズムは次のとおりです。

1) カードを K 個の等しい山に分けます。

2) 一番下の N / K カードは、同じ順序でパイル 1 に属します (最初のパイルの一番下のカードは、パイル 1 の一番下のカードです)。

3) 下から次の N / K 枚のカードはパイル 2 に属し、以下同様です。

4) シャッフルされたパイルの一番上のカードがパイル 1 の一番上のカードです。次のカードはパイル 2 の一番上のカードです。...、シャッフルされたパイルの K 番目のカードがパイル K の一番上のカードです。 (K + 1) 番目のカードは山 1 の一番上にあるカード、(K + 2) 番目は山 2 の一番上にあるカードなどです。

たとえば、N = 6、K = 3 の場合、1 度シャッフルすると、カードのデッキ「ABCDEF」(上から下) の順序は「ECAFDB」に変わります。

N と K が与えられた場合、パイルを元の順序に戻すために必要なシャッフルの最小回数は?

入力: 最初の行には、テスト ケースの数 T が含まれます。次の T 行には、それぞれ N と K の 2 つの整数が含まれます。

出力: 必要な最小数のシャッフルを含むテスト ケースごとに 1 つの T 行を出力します。デッキが元の順序に戻らない場合は、-1 を出力します。

制約:

  • K は N の因数になります。
  • T <= 10000
  • 2 <= K <= N <= 10^9

ネタバレ注意 - 自分で解決したい場合は、以下を読まないでください。

問題は次のように翻訳できます。

N 枚のカードのデッキを最初の順序に戻すために、K-way (完全)インシャッフルを実行する必要がある回数を求めてください。

この問題を解決するために、私は 2 つのアプローチを取りました。頭に浮かんだ最初のアプローチは次のとおりです。

  • 最初の順序での位置が与えられると、カードの次の位置が生成される式を見つける
  • 式を使用して、最初の山 (サイズは n / k) から各カードをシャッフルして最初の位置に戻す回数を決定します。
  • 以前に決定されたシャッフル数の最小公倍数を返します

この解の複雑さは O(n / k + max_number_of_suhffles) です。これが実際の実装です。これの問題は、最大時間を超えることです。そのため、ほぼ一定の時間で数値を取得できる式を探し始めました。

ここで最適化できたのは (たとえば、同じ順列サイクルで計算された値をキャッシュするためにいくつかのマップを使用したなど)、interviewstreet で 3/10 テストに合格することでした。


この実装を見つけました。これは、初期状態に戻るために必要なシャッフルの数が、N + 1 に対する Kの乗法次数であると想定しています。wiki から:

As a consequence of Lagrange's theorem, ordn(a) always divides φ(n). 

φ(n) はEuler totient function、ordn は群次数- 私たちが探しているものです。φ を使用してシャッフルの数を計算するこの論文を見つけましたが、これは k-way ではなく 2-way in-shuffle のみを対象としています。

この実装の手順は次のとおりです。

  • 素数のリストを事前計算 < 100 000
  • φ(N+1)その素因数から計算されます。
  • φ(N + 1)は、その素因数をすべての可能な方法で組み合わせることによって、 のすべての因数を決定しました。
  • x各因子を順番に試して、最小のもの を取得します。k ^ x % N + 1 = 1

この実装はGitHub にも投稿されています

これは非常に高速に実行されますが、自動グレーダーは、SPOJ と Interviewstreet の両方で、10 個のテストのうち 9 個を「不正解」に分類してくれます。

2 つの実装からの出力を比較してみましたが、入れたテストケース (既知の結果とランダム) では、2 つの実装は常に同じものを出力します。これは奇妙です。最初のアルゴリズムが正しいと確信しているので、2 番目のアルゴリズムも正しいはずだと思います。

「間違った答え」の分類は、コードの実行時エラーに起因する可能性がありますが、この原因として考えられるものは何もありません。

数をシャッフルしてもデックを初期状態に戻せない場合は考慮していませんでした。これは不可能であると理解しています。シャッフルの数が非常に多い場合でも、有限数の完全なシャッフルが最終的には最初の順序を復元します。

時間を割いてこれを読んでくれたなら、ありがとう。:) この問題に興味があります。解決したいと思います。

4

3 に答える 3

3

これは、私が紙で行ったいくつかの観察の後に思いついたものです。

class CardShuffle {
    private long k;
    private long n;
    private long kn;
    private long kn2;

    public CardShuffle(long k, long n) {
            //I omitted some checks done here
        this.k = k;
        this.n = n;
        this.kn = k / n;
        this.kn2 = k - kn;
    }

    public long shuffle() {
        long count = 0L;
        long next = 0L;
        do {
               //this can be further optimized
           next = kn2 - kn * (next % n) + (next / n); 
           ++count;
        } while((next != 0L) && (count < k));
        if(count > k)
           return -1;
        return count;
    }
}

結果は...

Testing 1000000 : 2
#ms: 3.121905
#ms: 1424.487191
#1: 9900 #2: 9900
Testing 1000000 : 5
#ms: 1.409955
#ms: 556.329366
#1: 2475 #2: 2475
Testing 1000000 : 10
#ms: 0.007823
#ms: 186.797204
#1: 12 #2: 12
Testing 1000000 : 20
#ms: 0.590298
#ms: 275.751527
#1: 4950 #2: 4950
Testing 1000000 : 25
#ms: 0.298642
#ms: 260.559372
#1: 2475 #2: 2475
Testing 1000000 : 40
#ms: 1.187581
#ms: 241.956729
#1: 9900 #2: 9900
Testing 1000000 : 50
#ms: 1.187581
#ms: 241.015548
#1: 9900 #2: 9900
Testing 9999999 : 41841
#ms: 14.499887
#ms: 1829.868042
#1: 125000 #2: 125000
Testing 9999999 : 3333333
#ms: 58.119398
#ms: 311.005728
#1: 500000 #2: 500000
Testing 9999999 : 13947
#ms: 52.704185
#ms: 2095.336418
#1: 500000 #2: 500000

この入力に対してテストされます...

10
1000000 2
1000000 5
1000000 10
1000000 20
1000000 25
1000000 40
1000000 50
9999999 41841
9999999 3333333
9999999 13947

最初#msは私の方法にかかったミリ秒単位の時間で、2番目はあなたのものです。 #1#2はそれぞれ結果です。

ところで、この入力は...

15
1000000000 2
1000000000 5
1000000000 10
1000000000 20
1000000000 25
1000000000 40
1000000000 50
1000000000 1000
1000000000 200000000
1000000000 250000000
1000000000 500000000
1000000000 50000000
999999999 1001001
999999999 37037037
999999999 333333333

私の方法は解決策を見つけます

Testing 1000000000 : 2
#ms: 71.360466
#1: 525780
Testing 1000000000 : 5
#ms: 68.987259
#1: 525780
Testing 1000000000 : 10
#ms: 0.008381
#1: 18
Testing 1000000000 : 20
#ms: 75.608492
#1: 525780
Testing 1000000000 : 25
#ms: 31.843154
#1: 262890
Testing 1000000000 : 40
#ms: 33.014531
#1: 262890
Testing 1000000000 : 50
#ms: 84.27384
#1: 525780
Testing 1000000000 : 1000
#ms: 0.006705
#1: 6
Testing 1000000000 : 200000000
#ms: 53.991778
#1: 525780
Testing 1000000000 : 250000000
#ms: 43.765898
#1: 262890
Testing 1000000000 : 500000000
#ms: 54.457201
#1: 525780
Testing 1000000000 : 50000000
#ms: 68.080999
#1: 525780
Testing 999999999 : 1001001
#ms: 115.060154
#1: 1000000
Testing 999999999 : 37037037
#ms: 5783.539528
#1: 50000000
Testing 999999999 : 333333333
#ms: 5391.880532
#1: 50000000

ただし、私の古くて遅いラップトップでは、最初のものでメモリが不足します。

このアプローチはまだ検証していませんが、機能しているように見えます。一部の入力で失敗するかどうかを試してみてください。私はそれを感謝します。

私が式をどのように開発したかについてもう少し興味がある場合は、コメントを残してください。

私もソリューションを Interviewstreet に提出しました、制限時間の制約により 4 番目のテストケースで失敗しました。

私はすぐにacプログラムを試して、ここに報告します。

于 2012-08-22T13:20:03.057 に答える
0

N と K が与えられたとき、それが生成する順列を計算し、それがhttp://en.wikipedia.org/wiki/Cycle_notationでどのように見えるかを計算します。循環表記では、順列は互いに素な循環の積です。たとえば、A->E->D->F->B->C->A であるため、ABCDEF => ECAFDB は (AEDFBC) です。順列がこのような単一のサイクルである場合、サイクルの長さは、同じ場所に戻るためにそれを繰り返す必要がある回数です。順列が (ABC)(DE)(F) のように複数の互いに素なサイクルの積である場合、繰り返す必要がある回数は、個々の長さのhttp://en.wikipedia.org/wiki/Least_common_multipleです。サイクル。

于 2012-08-21T04:35:23.810 に答える
-1
#include <iostream>
using namespace std;
int main() 
{
    int t, m , n, c, p, k, pos, cases;

    cin>>cases;

    while(cases--)
    {
        cin>>t;
        cin>>k;

        p = t/k;
        pos = 1;
        c = 0;

        do
        {
            c++;
            m = (pos - 1)%p + 1;
            n = k - (pos - m)/p;
            pos = k*(m-1) + n;

        }while(pos!=1);

        cout<<c<<endl;


    }
    return 0;
}
于 2012-09-20T14:22:48.420 に答える