kostumブログ

勉強したことやノート代わりのアウトプットに使っています。

project Euler 021

環境

問題

d(n) を n の真の約数の和と定義する. (真の約数とは n 以外の約数のことである. )もし, d(a) = b かつ d(b) = a (ab のとき) を満たすとき, ab友愛数(親和数)であるという.

例えば, 220 の約数は 1, 2, 4, 5, 10, 11, 20, 22, 44, 55, 110 なので d(220) = 284 である.また, 284 の約数は 1, 2, 4, 71, 142 なので d(284) = 220 である.

それでは10000未満の友愛数の和を求めよ.

考え方

  1. 1から10000までの数を数え上げる。
  2. その数の約数の和を計算する
  3. 2.の和からさらに約数の和を計算する
  4. 数え上げの数字と3.の約数の和が等しい時、それらの数字を友愛数とし、配列に格納しておく
  5. 数え上げが終了した後、配列内の友愛数の和を計算する

コード

let limit = 10000;
let yuaisuList: number[] = [];
for (let a = 2; a < limit; a++) {
  let yakusuAList = [1];
  let yakusuBList = [1];
  for (let i = 2; i < a; i++) {
    if (a % i === 0) {
      yakusuAList.push(i);
    }
  }
  let sumA = yakusuAList.reduce((a, b) => a + b);
  for (let i = 2; i < sumA; i++) {
    if (sumA % i === 0) {
      yakusuBList.push(i);
    }
  }
  let sumB = yakusuBList.reduce((a, b) => a + b);
  if (
    a === sumB &&
    !yuaisuList.find(yuaisu => yuaisu === a) &&
    sumA !== sumB
  ) {
    yuaisuList.push(a);
    yuaisuList.push(sumA);
  }
}
return yuaisuList.reduce((a, b) => a + b);

説明

  • 数え上げ数は2から始める。その場合、約数リストには必ず1が格納されている
  • まず、数え上げ数の約数を算出し、yakusuAlistに格納する
  • yakusuAlistの和を計算し、今度はその和の約数を算出し、yakusuBlistに格納する
  • yakusuBlistの和を計算し、友愛数であるかどうかを条件分岐し、友愛数であれば、友愛数リストに格納する
  • 数え上げ終了後、友愛数リストの和を計算する

project Euler 020

環境

問題

n × (n - 1) × ... × 3 × 2 × 1 を n! と表す.

例えば, 10! = 10 × 9 × ... × 3 × 2 × 1 = 3628800 となる.この数の各桁の合計は 3 + 6 + 2 + 8 + 8 + 0 + 0 = 27 である.

では, 100! の各位の数字の和を求めよ.

注: Problem 16 も各位の数字の和に関する問題です。解いていない方は解いてみてください。

考え方

  1. 100!を計算する(bigint型)。
  2. 計算結果の各位の数字を取り出す
  3. 和を計算する

コード

let n = BigInt(100);
let sum = BigInt(1);
for (let i = n; n > BigInt(0); n--) {
  sum = sum * n;
}
let array = [];
for (let j = 0; j <= String(sum).length; j++) {
  array.push(String(sum).charAt(j));
}
return array.reduce((a, b) => {
  return String(Number(a) + Number(b));
});

説明

  • 100!を計算する(number型では計算できないため、bigint型を使う)
  • 計算結果を文字列に変換し、各位の数を取り出す
  • 取り出した文字を数字に再変換し、和を計算する

project Euler 019

環境

問題

次の情報が与えられている.

  • 1900年1月1日は月曜日である.
  • 9月, 4月, 6月, 11月は30日まであり, 2月を除く他の月は31日まである.
  • 2月は28日まであるが, うるう年のときは29日である.
  • うるう年は西暦が4で割り切れる年に起こる. しかし, 西暦が400で割り切れず100で割り切れる年はうるう年でない.

20世紀(1901年1月1日から2000年12月31日)中に月の初めが日曜日になるのは何回あるか?

考え方

  1. 変数に西暦年、月、日を設定する
  2. 日は「9月, 4月, 6月, 11月は30日, 2月を除く他の月は31日。」「2月は28日まであるが, うるう年のときは29日」「うるう年は西暦が4で割り切れる年に起こる。しかし、西暦が400で割り切れず100で割り切れる年はうるう年でない」条件を満たしつつ、for文で回す。

コード

// 0 = 日、1 = 月、2 = 火、3 = 水、4 = 木、5 = 金、6 = 土
let date = 1;
let limitDay = 31;
let sum = 0;
for (let year = 1900; year < 2001; year++) {
  for (let month = 1; month < 13; month++) {
    if (month === 9 || month === 4 || month === 6 || month === 11) {
      limitDay = 30;
    } else if (
      month === 1 ||
      month === 3 ||
      month === 5 ||
      month === 7 ||
      month === 8 ||
      month === 10 ||
      month === 12
    ) {
      limitDay = 31;
    } else if (month === 2) {
      if (year % 400 !== 0 && year % 100 === 0) {
        limitDay = 28;
      } else if (year % 4 === 0) {
        limitDay = 29;
      } else {
        limitDay = 28;
      }
    }
    for (let day = 1; day <= limitDay; day++) {
      if (year > 1900 && day === 1 && date === 0) {
        sum = sum + 1;
      }
      date = date + 1;
      // 曜日計算
      if (date === 7) {
        date = 0;
      }
    }
  }
}
return sum;

説明

  • 曜日、月によって変化する日、月の初めが日曜日になる回数を変数に設定する。
  • for 文で1900年から2000年までループする。
  • その中で、for 文で1月から12月まで回す。
  • この時、月によって変わる最後の日を条件分岐で設定する。
  • さらにその中で、for 文で1日〜28 or 29 or 30 or 31日を回す。
  • 日の for 文内で、月の初めが日曜日になる時かつ1901年以上の時、数える。
  • さらに、曜日計算をする

project Euler 017

環境

問題

1 から 5 までの数字を英単語で書けば one, two, three, four, five であり, 全部で 3 + 3 + 5 + 4 + 4 = 19 の文字が使われている.

では 1 から 1000 (one thousand) までの数字をすべて英単語で書けば, 全部で何文字になるか.

: 空白文字やハイフンを数えないこと. 例えば, 342 (three hundred and forty-two) は 23 文字, 115 (one hundred and fifteen) は20文字と数える. なお, "and" を使用するのは英国の慣習.

考え方

  1. 1(one) ~ 10(ten), eleven, twelve, thirteen, fourteen, fifteen, sixteen, seventeen, eighteen, nineteen, twenty, thirty, forty, fifty, sixty, seventy, eighty, ninety, hundred, thousand, and を文字数化する
  2. 1から1000までを数え上げる
  3. 数え上げ数を分解して、英単語で表す
  4. 英単語を文字数に変換する
  5. 合計値を計算する

コード

let eitangoList = [
  {
    number: 0,
    eitango: ""
  },
  {
    number: 1,
    eitango: "one"
  },
  {
    number: 2,
    eitango: "two"
  },
  {
    number: 3,
    eitango: "three"
  },
  {
    number: 4,
    eitango: "four"
  },
  {
    number: 5,
    eitango: "five"
  },
  {
    number: 6,
    eitango: "six"
  },
  {
    number: 7,
    eitango: "seven"
  },
  {
    number: 8,
    eitango: "eight"
  },
  {
    number: 9,
    eitango: "nine"
  },
  {
    number: 10,
    eitango: "ten"
  },
  {
    number: 11,
    eitango: "eleven"
  },
  {
    number: 12,
    eitango: "twelve"
  },
  {
    number: 13,
    eitango: "thirteen"
  },
  {
    number: 14,
    eitango: "fourteen"
  },
  {
    number: 15,
    eitango: "fifteen"
  },
  {
    number: 16,
    eitango: "sixteen"
  },
  {
    number: 17,
    eitango: "seventeen"
  },
  {
    number: 18,
    eitango: "eighteen"
  },
  {
    number: 19,
    eitango: "nineteen"
  },
  {
    number: 20,
    eitango: "twenty"
  },
  {
    number: 30,
    eitango: "thirty"
  },
  {
    number: 40,
    eitango: "forty"
  },
  {
    number: 50,
    eitango: "fifty"
  },
  {
    number: 60,
    eitango: "sixty"
  },
  {
    number: 70,
    eitango: "seventy"
  },
  {
    number: 80,
    eitango: "eighty"
  },
  {
    number: 90,
    eitango: "ninety"
  },
  {
    number: 100,
    eitango: "hundred"
  },
  {
    number: 1000,
    eitango: "thousand"
  }
];

let sum = 0;
for (let i = 1; i <= 1000; i++) {
  let eitangosu: number | undefined = 0;

  let iStr = String(i);
  let niketa = String(i).substring(-2);
  let sanketa = String(i).substring(-3);

  // 一桁
  if (iStr.length === 1) {
    eitangosu = eitangoList.find(eigo => {
      return i === eigo.number;
    })?.eitango.length;
    if (eitangosu) {
      sum = sum + eitangosu;
    }
  }
  // 二桁
  if (iStr.length === 2) {
    if (i < 21) {
      eitangosu = eitangoList.find(eigo => {
        return i === eigo.number;
      })?.eitango.length;
      if (eitangosu) {
        sum = sum + eitangosu;
      }
    } else {
      let niketame = niketa.charAt(0);
      let hitoketame = niketa.charAt(1);
      let ninokurai = niketame + "0";
      let niketameeitango = eitangoList.find(eigo => {
        return eigo.number === Number.parseInt(ninokurai);
      });
      let hitoketameeitango = eitangoList.find(eigo => {
        return eigo.number === Number.parseInt(hitoketame);
      });
      if (!niketameeitango || !hitoketameeitango) return;

      sum =
        sum +
        niketameeitango.eitango.length +
        hitoketameeitango?.eitango.length;
    }
  }
  // 三桁
  if (iStr.length === 3) {
    let sanketame = sanketa.charAt(0);
    let niketame = niketa.charAt(1);
    let hitoketame = niketa.charAt(2);

    let sannokurai = sanketame;
    let ninokurai = niketame + "0";

    let sanketameeitango =
      eitangoList.find(eigo => {
        return eigo.number === Number.parseInt(sannokurai);
      })?.eitango + "hundredand";
    let niketameeitango = eitangoList.find(eigo => {
      return eigo.number === Number.parseInt(ninokurai);
    });
    let hitoketameeitango = eitangoList.find(eigo => {
      return eigo.number === Number.parseInt(hitoketame);
    });
    if (!niketameeitango || !hitoketameeitango) return;
    sum =
      sum +
      sanketameeitango.length +
      niketameeitango.eitango.length +
      hitoketameeitango?.eitango.length;
  }
  if (iStr.length === 4) {
    let yonnokurai = "onethousand";
    sum = sum + yonnokurai.length;
  }
}
return sum;

説明

  • 英単語に対応する数を連想配列として設定する。
  • 数え上げ数の1桁目、2桁目、3桁目の数字を文字に変換し、それぞれの長さを算出して、和を計算する

project Euler 016

環境

問題

215 = 32768 であり, 各位の数字の和は 3 + 2 + 7 + 6 + 8 = 26 となる. 同様にして, 21000 の各位の数字の和を求めよ.

考え方

  1. 21000を計算する(bigint型)。
  2. 計算結果の各位の数字を取り出す
  3. 和を計算する

コード

let two = BigInt(2);
let ruijousu = 1000;
let result = BigInt(1);
for (let i = 1; i <= ruijousu; i++) {
  result = result * two;
}
let resultStr = String(result);
let resultList = [];
for (let j = 0; j < resultStr.length; j++) {
  resultList.push(resultStr.charAt(j));
}
let sum = 0;
resultList.forEach(result => {
  sum = sum + Number.parseInt(result);
});
return sum;

説明

  • 21000を計算する(number型では計算できないため、bigint型を使う)
  • 計算結果を文字列に変換し、各位の数を取り出す
  • 取り出した文字を数字に再変換し、和を計算する

project Euler 015

環境

問題

2×2 のマス目の左上からスタートした場合, 引き返しなしで右下にいくルートは 6 つある.

https://projecteuler.net/project/images/p015.png

では, 20×20 のマス目ではいくつのルートがあるか.

考え方

  1. 最短経路の算出の仕方は、例の場合は、4C2 = (4 * 3) / (2 * 1)
  2. 20×20の場合は、40C20

コード

const tate = 20;
const yoko = 20;
let bunbo = 1;
let bunshi = 1;
for (let i = tate + yoko; i > tate; i--) {
  bunbo = bunbo * i;
}
for (let i = tate; i > 0; i--) {
  bunshi = bunshi * i;
}
return Math.floor(bunbo / bunshi);

説明

  • 組み合わせの計算をコードで書く

    $$nCr = ( (n + r) * (n + r - 1) * (n + r - 2) * ( ... ) * n ) / ( r * (r - 1) * (r - 2) * ( ... ) * 1 )$$

  • 最後に小数点を丸めて計算する