2013年5月18日土曜日

C# で Project Euler に挑戦: Problem 6

問題

二乗和の差

最初の10個の自然数について, その二乗の和は,

12 + 22 + ... + 102 = 385

最初の10個の自然数について, その和の二乗は,

(1 + 2 + ... + 10)2 = 3025

これらの数の差は 3025 - 385 = 2640 となる.

同様にして, 最初の100個の自然数について二乗の和と和の二乗の差を求めよ.

ぼくの解答

static void Main(string[] args)
{
    int n = 100;

    var numbers = Enumerable.Range(1, n);

    var sum = numbers.Sum();
    var sumSquare = sum * sum; // 和の二乗を求めます。
    var squreSum = numbers.Sum(i => i * i); // 二乗数の和を求めます。

    var answer = sumSquare - squreSum;

    Console.WriteLine(answer);
    Console.ReadLine();
}

解説

あまり説明するところがありません... ごく普通に 1 から 100 までの総和の二乗と、二乗の総和を求めています。

この問題はなんか簡単ですね。もっと面白い解き方があるのかな。

2013年5月16日木曜日

C# で Project Euler に挑戦: Problem 5

問題

最小の倍数

2520 は 1 から 10 の数字の全ての整数で割り切れる数字であり, そのような数字の中では最小の値である.

では, 1 から 20 までの整数全てで割り切れる数字の中で最小の正の数はいくらになるか.

ぼくの解答

static void Main(string[] args)
{
    int n = 20;

    int multiple = 1;
    int counter = 1;

    var numbers = Enumerable.Range(1, n);

    foreach (var i in numbers)
    {
        while (counter % i != 0)
        {
            counter += multiple;
        } 

        multiple = counter;
    }

    Console.WriteLine(multiple);
    Console.ReadLine();
}

解説

最小公倍数を求める問題です。単純に 1 から 20 まで順番に最小公倍数を見つけていきます。

最小公倍数を multiple として初期値を 1 とします。カウンタ用の変数(counter)も初期値を 1 にします。

  1. この時点の最小公倍数(1) と 1 の最小公倍数を求める。
    • 1 / 1 = 1 ... 0 => 割り切れるので最小公倍数1 に更新。
  2. この時点の最小公倍数(1) と 2 の最小公倍数を求める。
    • 1 / 2 = 0 ... 1 => 割り切れないので countermultiple を加算。
    • 2 / 2 = 1 ... 0 => 割り切れるので最小公倍数2 に更新。
  3. この時点の最小公倍数(2) と 3 の最小公倍数を求める。
    • 2 / 3 = 0 ... 2 => 割り切れないので countermultiple を加算。
    • 4 / 3 = 1 ... 1 => 割り切れないので countermultiple を加算。
    • 6 / 3 = 2 ... 0 => 割り切れるので最小公倍数6 に更新。
  4. この時点の最小公倍数(6) と 4 の最小公倍数を求める。
    • 6 / 4 = 1 ... 2 => 割り切れないので countermultiple を加算。
    • 12 / 4 = 3 ... 0 => 割り切れるので最小公倍数12 に更新。
  5. この時点の最小公倍数(12) と 5 の最小公倍数を求める。
    • 12 / 5 = 2 ... 2 => 割り切れないので countermultiple を加算。
    • 24 / 5 = 4 ... 4 => 割り切れないので countermultiple を加算。
    • 36 / 5 = 7 ... 1 => 割り切れないので countermultiple を加算。
    • 48 / 5 = 9 ... 3 => 割り切れないので countermultiple を加算。
    • 60 / 5 = 12 ... 0 => 割り切れるので最小公倍数60 に更新。
  6. この時点の最小公倍数(60) と 6 の最小公倍数を求める。
    • 60 / 6 = 10 ... 0 => 割り切れるので最小公倍数60 に更新。
  7. ... という風に続く。

他のプローチ

調べたら他にも良さそうなやり方がありました。最大公約数を元に解くのとか、数学っぽくていいですね。ぼくの頭では思いつきませんでした...

2013年5月13日月曜日

C# で Project Euler に挑戦: Problem 4

問題

最大の回文積

左右どちらから読んでも同じ値になる数を回文数という. 2桁の数の積で表される回文数のうち, 最大のものは 9009 = 91 × 99 である.

では, 3桁の数の積で表される回文数のうち最大のものを求めよ.

ぼくの解答

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main(string[] args)
    {
        var threeDigitNumbers = Enumerable.Range(100, 900).ToList();
        var answer = threeDigitNumbers
            .SelectMany(i => threeDigitNumbers.Select(j => i * j))
            .Where(i => i.IsPalindrome())
            .Max();

        Console.WriteLine(answer);
        Console.ReadLine();
    }
}

public static class NumberHelper
{
    public static bool IsPalindrome(this int n)
    {
        return n.Digits().SequenceEqual(n.Digits().Reverse());
    }

    public static IEnumerable<int> Digits(this int n)
    {
        if (n == 0)
            yield return 0;

        const int radix = 10;
        var q = n;

        while (q != 0)
        {
            yield return q % radix;
            q /= radix;
        }
    } 
}

解説

この問題のポイントは、どうやって回文数かどうか判定するか、でしょうか。以下のどちらかになると思います。

  1. 文字列で判定する
  2. 各桁の数値をリストにして判定する

文字列でやるほうが簡単ですが、せっかくなので数値の桁を列挙する Digits() メソッドを作って解いてみました。

ある数値の桁の求め方

求めたい進法の基数で割っていって余りを取り出すと桁を求めることができます。例えば 4603 の場合、手順は以下です。

  1. 4603 / 10 = 460 ... 3 (商を次の割られる数へ回します)
  2. 460 / 10 =  46 ... 0
  3.   46 / 10 =   4 ... 6
  4.    4 / 10 =   0 ... 4 (商が 0 になったら終了です)
回文数かどうか

上記のように桁のリストを求めるか、文字列に変換して内容を反転させて元のものと同じかどうか調べればよいですね。

今回は int の拡張メソッドに IsPalindrome というメソッドを用意しています。内容は簡単です。

上記の Digits メソッドで桁を列挙し、Reverse メソッドで反転させたものと同じかを、SequenceEqual メソッドで調べます。SequenceEqual メソッドが true を返せば回文数です。

三桁の数字の積

単純にループしても良いですが、せっかく C# なので LINQ です。

はじめに三桁の数字のリストを作ります。Enumerable.Range(100, 900) で簡単ですね。(100 ∼ 999)

組み合わせを作ってそれらをかければいいので、SelectMany と Select メソッドを使って簡単に記述できます。

実行効率

この解は実行効率が悪いです。三桁の数字の組み合わせを全部作ってしまう点がネックですね。
実行効率でいえば、999 * 999 から 999 * 998, 999 * 997 ... とデクリメントしながら積を求め、回文数になった時点で処理を中断するほうが良いです。

2013年5月9日木曜日

C# で Project Euler に挑戦: Problem 3

問題

最大の素因数

13195 の素因数は 5, 7, 13, 29 である.

600851475143 の素因数のうち最大のものを求めよ.

ぼくの解答

static void Main(string[] args)
{
    var n = 600851475143;

    // まず約数を求め、その中から素数を見つけることで素因数を獲得します。
    var answer = n.Divisors().Where(i => i.IsPrime()).Max();
    Console.WriteLine(answer);

    Console.ReadLine();
}
public static class NumberHelper
{
    public static IEnumerable<long> Divisors(this long n)
    {
        long minor = 1;
        long greater = n;
        var counter = minor;

        while (counter < greater)
        {
            if (n % counter == 0)
            {
                minor = counter;
                greater = n / minor;

                yield return minor;
                yield return greater;
            }

            counter++;
        }            
    } 

    public static bool IsPrime(this long n)
    {
        if (n < 2) return false;
        if (n == 2) return true;
        if (n%2 == 0) return false;

        for (var i = 3; i*i < n; i += 2)
        {
            if (n%i == 0) return false;
        }

        return true;
    }
}

解説

素因数は約数を求めた後、その中から素数を見つけるのが簡単そうだったので、約数を求めるメソッドと、簡単な素数判定メソッドを拡張メソッドで用意しました。

素因数のリストが手に入れば、あとは最大値を返すだけです。

約数の求め方

約数を求めるときは 1 から順に試しに割っていきますが、もし割り切れたら、割った数(カウンタ)除算の結果の両方が約数なので、同時に約数として返していきます。

このとき、除算の結果(商)より大きい約数が今後登場することはないので、終了値を商に置き換えて行きます。

例えば 20 の約数を求めるときは、

  1. 1 で割ってみる -> 20 / 1 = 20 -> 120 が約数 -> カウンタの終了値を 20
  2. 2 で割ってみる -> 20 / 2 = 10 -> 210 が約数 -> カウンタの終了値を 10
  3. 3 で割ってみる -> 割り切れないので次
  4. 4 で割ってみる -> 20 / 4 = 5 -> 45 が約数 -> カウンタの終了値を 5
  5. カウンタが終了値(5)になったので終わり。約数は、1, 20, 2, 10, 4, 5

という感じです。

素数判定

簡単な試し割りです。一度約数を見つけて、その後素数判定をしているので簡単な試し割りで十分かなということで。

TFT 10.14 Peeba Comp

こちらのガイドの自分用まとめです。 https://www.reddit.com/r/CompetitiveTFT/comments/hraunp/tft_1014_break_the_meta_new_peeba_comp_set_35/ 難しいですが完成すると非常に強く、プレ...