亀岡的プログラマ日記

京都のベッドタウン、亀岡よりだらだらとお送りいたします。

Project Euler Prob.5 をネタにグルグル考えるだけ。

前に書いたのの続きです。

	var numList = Enumerable.Range(1,20);
	var dividerList = new List<int>();
	
	for(int i = 2; i <= 20; i++)
	{
		while(numList.Any( ele => ele % i == 0))
		{
			dividerList.Add(i);
			numList = numList.Select( ele => ele % i == 0 ? ele / i : ele).ToList();//Enumerate immediately
		}
	}
	var val = 1;
	dividerList.ForEach(ele => val *= ele);

へろへろ状態で書いたコードなので酷い。というわけでちょっと改善しましょう。リストに入ってる数字を2~Nまでの数字で割ってみて、もし一つでも割り切れるなら、その数をリストに保存し、さらにリストの中でその数で割り切れるものがあれば、それ自身も割っておく、という塩梅です。結局何をやってるかというと最小公倍数を出しているだけですね。なので最後にリストの中身を掛けておきます。

・・・はい、リストに保存する必要全然ないですね。リストにする代わりに、もう逐次結果に掛けていきましょう。

	var numList = Enumerable.Range(1,20);
	var val = 1;
	
	for(int i = 2; i <= 20; i++)
	{
		while(numList.Any( ele => ele % i == 0))
		{
			val *= i; //掛けるだけでいいじゃん
			numList = numList.Select( ele => ele % i == 0 ? ele / i : ele).ToList();
		}
	}


あとは、どうにもfor文がイヤな感じです。まず2~20というループには本来意味がないはずで、要はリストの中に入っている中で、割って意味がある数字、要するに1以外の数字を取ってくればいいわけです。そしてリストの中身が全部1になっちゃったらおしまい、と。

	var numList = Enumerable.Range(1,20);
	var val=1;
	var i =  2;
	do
	{
		while(numList.Any( ele => ele % i == 0))
		{
			val *= i;
			numList = numList.Select( ele => ele % i == 0 ? ele / i : ele).ToList();
		}
		i = numList.FirstOrDefault ((ele) => ele > 1); //1より大きい要素の最初を取得
	}while(i != 0);

do while文でなんとか押し込めました。うーん。もう一息どうにかなんね??
あぁ、計算コストの増大さえ目をつぶれば、Whileループは一重で住みますね。その数で割り切れなくなるまで続ける、なんて考えずに今の値で割りきってしまって次へ進めば効率は多少落ちますが同じ結果にはなりますので。とすると、Whileループ一重に。

	var numList = Enumerable.Range(1,20);
	var val=1;
	while(numList.Any(ele => ele > 1)) //リストの中身が全部1になるまで続ける
	{
                //1より大きい最初の数を持ってくる
		var i = numList.First(e => e > 1);
		val *= i;
                //その数で数列全体を割る(割り切れない要素は割らないでおく)
		numList = numList.Select( ele => ele % i == 0 ? ele / i : ele).ToList();
	}

とりあえず、こんくらいかなあ。絶対ワンライナーできると思うんだけどなあ。。。