ズンドコキヨシをLINQでゴリ押してみる
概要という名の感想
- 正直LINQで先駆者がいるので、別の面白いアイデアがなかなか浮かばなかった
- でも
Random().NextBytes()
というアイデアを得て、1時間くらいで作成 - ソースは簡略化を目指して、indexの配列(List)を持たせることで解決
- やっぱりもうちょっとスマートに出来るんじゃないかと勘ぐってます
ズンドコキヨシとは
Javaの講義、試験が「自作関数を作り記述しなさい」って問題だったから
— てくも (@kumiromilk) 2016年3月9日
「ズン」「ドコ」のいずれかをランダムで出力し続けて「ズン」「ズン」「ズン」「ズン」「ドコ」の配列が出たら「キ・ヨ・シ!」って出力した後終了って関数作ったら満点で単位貰ってた
プログラミングのスキルを簡易的に見る、「FizzBuzz」問題と同じような問題として、その発想のセンスと共に話題となったアルゴリズム問題です。
既にまとめとなっているように、数々の言語(中には言語なのか判別できないものも)でチャレンジされており、良問として親しまれているようです。
実は、既に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: ズンズンズンズンドコ