LoginSignup
2
3

More than 5 years have passed since last update.

ズンドコキヨシをLINQでゴリ押してみる

Last updated at Posted at 2018-07-09

ズンドコキヨシをLINQでゴリ押してみる

概要という名の感想

  • 正直LINQで先駆者がいるので、別の面白いアイデアがなかなか浮かばなかった
  • でもRandom().NextBytes()というアイデアを得て、1時間くらいで作成
  • ソースは簡略化を目指して、indexの配列(List)を持たせることで解決
  • やっぱりもうちょっとスマートに出来るんじゃないかと勘ぐってます

ズンドコキヨシとは

プログラミングのスキルを簡易的に見る、「FizzBuzz」問題と同じような問題として、その発想のセンスと共に話題となったアルゴリズム問題です。

ズンドコキヨシまとめ - Qiita

既にまとめとなっているように、数々の言語(中には言語なのか判別できないものも)でチャレンジされており、良問として親しまれているようです。

実は、既にLINQを用いた回答例は出ているのですが、yieldなどまだ慣れない内容もあったので、自分なりにLINQだけを用いたアイデアでチャレンジしてみました。

前回のLINQ: バーコードのチェックディジットをLINQでゴリ押してみる - Qiita

ポイント

  • 「ズン」「ドコ」「キ・ヨ・シ!!」の部分文字列の配列を先に用意する
  • index(int型)の配列を答えにもち、乱数を保持する配列も0,1の2値をとるようにする
  • 前回も利用した、比較順としてのリストをEnumerable.Rangeで生成する(for文の省略)

新しい内容

  • results.All(x => x)はbool型のリストがすべてtrueであれば、結果もtrueである
  • string.Concat()IEnumerate<string>も入力として結合できる
  • Random().NextBytes(bytes[])のため、byte配列を用意しておく必要がある

ソースコード全文

Zundoko.cs
private static void Zundoko()
{
    var output = new StringBuilder();
    var rnd = new Random();

    string[] seedArr = new string[] { "ズン", "ドコ", "キ・ヨ・シ!!" };    // 出力の配列
    var answerArr = new int[] { 0, 0, 0, 0, 1 }.ToList();   // 正解の配列(ToListはindexのため利用)
    var idxs = Enumerable.Range(0, answerArr.Count);        // 整列のためのindex
    var resBytes = new byte[answerArr.Count];               // 乱数を格納するbyte配列

    while (true)
    {
        rnd.NextBytes(resBytes);    // byte列を一括で取得
        var rdmList = resBytes.Select(x => x % 2).ToList();                     // Mod 2で0,1のindexに変換
        var results = idxs.Select(idx => answerArr[idx].Equals(rdmList[idx]));  // 並び順の比較

        // results(IEnumerate<bool>)の結果がすべてtrueなら
        if (results.All(x => x))
        {
            // キ・ヨ・シ!!のindexを追加
            answerArr.Add(2);
            var resStr = string.Concat(answerArr.Select(x => seedArr[x]));  // 出力の文字列を生成
            output.AppendLine(resStr);
            break;
        }
        else
        {
            var resStr = string.Concat(rdmList.Select(x => seedArr[x]));    // 出力の文字列を生成
            output.AppendLine(resStr);
        }
    }

    Debug.Print(output.ToString());     // 結果の出力
}

結果

ドコドコズンズンドコ
ズンドコズンドコズン
ドコドコドコズンドコ
ドコズンズンドコズン
ドコズンズンドコドコ
ズンドコドコドコズン
ズンズンドコドコドコ
ズンズンズンドコドコ
ズンドコズンドコズン
ズンズンドコズンズン
ズンドコドコドコドコ
ドコズンズンドコズン
ズンドコズンズンズン
ドコドコドコズンドコ
ドコズンドコズンズン
ドコズンドコドコズン
ズンドコドコズンズン
ドコドコドコズンズン
ズンドコドコズンドコ
ズンドコドコドコズン
ズンドコドコズンドコ
ズンドコドコドコドコ
ドコズンドコズンズン
ズンドコズンドコズン
ズンズンズンドコドコ
ドコズンドコズンドコ
ズンズンズンズンズン
ドコズンズンズンズン
ズンズンズンズンドコキ・ヨ・シ!!

その他の方法

新しい内容については、近日中にまとめます。

キュー(Queue)を利用する

Zundoko2.cs
private static string Zundoko2()
{
     string[] seedArr = new string[] { "ズン", "ドコ", "キ・ヨ・シ!!" };
     var answerArr = new int[] { 0, 0, 0, 0, 1 }.ToList();
     var resQueue = new Queue<int>();
     var rnd = new Random();
     int rndTimes = 0;
     string resStr = string.Empty;
     sw.Reset();
     sw.Start();

     while (!resQueue.Contains(2))
     {
        rndTimes++;
        resQueue.Enqueue(rnd.Next(0, 2));

        if (resQueue.Count < 5)
        {
            continue;
        }

        // 最後の5個を取り出す
        var lastFive = resQueue.Skip(resQueue.Count - 5);

        // 答えと並びが一緒であること
        if (lastFive.SequenceEqual(answerArr))
        {
            resQueue.Enqueue(2);
            resStr = string.Concat(lastFive.Select(x => seedArr[x]));
        }
    }

    sw.Stop();
    return string.Format("{0}, {1}, {2}", rndTimes, sw.ElapsedMilliseconds, resStr);
}

スタック(Stack)を利用して一般化

Zundoko4.cs
private static string Zundoko4(string inputStr, string lastStr = "")
{
     // ここが重要
     // inputStrを分解し、重複を削除してから結合
     var seedArr = string.Concat(inputStr.Select(x => x).Distinct());  
     // inputStrをseedArrのindexに置き換え  
     var answerArr = inputStr.Select(x => seedArr.IndexOf(x));    

     var resStack = new Stack<int>();
     int lastIdx = answerArr.Max() + 1;
     var rnd = new Random();
     int rndTimes = 0;
     string resStr = string.Empty;
     sw.Reset();
     sw.Start();

     Debug.Print("seedArr: {0}", seedArr);
     while (!resStack.Contains(lastIdx) && sw.ElapsedMilliseconds < 1000)
     {
        rndTimes++;
        // 0~lastIdx未満の整数を追加
        resStack.Push(rnd.Next(0, lastIdx));
        // 比較用に反転
        var revStack = resStack.Reverse();

        // 答えと並びが一緒であること
        if (!revStack.SequenceEqual(answerArr.Take(resStack.Count)))
        {
            resStack.Pop();
        }
        else if (resStack.Count == answerArr.Count())
        {
            resStack.Push(lastIdx);
            resStr = string.Concat(inputStr, lastStr);
        }
     }

     sw.Stop();
     return string.Format("{0}, {1}, {2}", rndTimes, sw.ElapsedMilliseconds, resStr);
 }

おまけ:Stackの場合の結果の変化

1: コ
2: ド
3: ド
4: ン
5: ズ
6: ズド
7: ズズ
8: ズン
9: ズンン
10: ズンド
11: ズンン
12: ズンン
13: ズンコ
14: ズンン
15: ズンド
16: ズンド
17: ズンド
18: ズンズ
19: ズンズド
20: ズンズド
21: ズンズズ
22: ズンズド
23: ズンズン
24: ズンズンズ
25: ズンズンズン
26: ズンズンズンズ
27: ズンズンズンズズ
28: ズンズンズンズコ
29: ズンズンズンズド
30: ズンズンズンズズ
31: ズンズンズンズズ
32: ズンズンズンズン
33: ズンズンズンズンン
34: ズンズンズンズンコ
35: ズンズンズンズンド
36: ズンズンズンズンドド
37: ズンズンズンズンドン
38: ズンズンズンズンドド
39: ズンズンズンズンドコ
2
3
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3