トップページに戻る
次のC#のサンプルへ
前のC#のサンプルへ
Problem122 効率的なべき乗計算
問題
nの15乗を求めるのに最も単純な方法では 14 回の掛け算が必要である.
n * n * ・・・ * n = nの15乗
しかし2進法を用いれば, 6 回の掛け算で計算できる.
n * n = nの2乗
nの2乗 * nの2乗 = nの4乗
nの4乗 * nの4乗 = nの8乗
nの8乗 * nの4乗 = nの12乗
nの12乗 * nの2乗 = nの14乗
nの14乗 * n = nの15乗
ところがたった 5 回の掛け算のみでも計算できる.
n * n = nの2乗
nの2乗 * n = nの3乗
nの3乗 * nの3乗 = nの6乗
nの6乗 * nの6乗 = nの12乗
nの12乗 * nの3乗 = nの15乗
m(K) を nのK乗を求めるのに必要最低限な掛け算の回数と定義する.
たとえば m(15) = 5 である.
1 <= K <= 200 に対し、シグマ m(K) を求めよ.
ソース
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
var sw = System.Diagnostics.Stopwatch.StartNew();
int Answer = 0;
int PrevM = 0;
for (int I = 1; I <= 200; I++) {
int CurrM = DeriveM(I, PrevM);
Console.WriteLine("m({0})={1}。経過時間={2}", I, CurrM, sw.Elapsed);
Answer += CurrM;
PrevM = CurrM;
}
Console.WriteLine("Answer={0}", Answer);
}
struct JyoutaiDef
{
internal int Level;
internal HashSet<int> ValSet;
}
static int DeriveM(int pK, int pPrevM)
{
var Stk = new Stack<JyoutaiDef>();
JyoutaiDef WillPush;
WillPush.Level = 0;
WillPush.ValSet = new HashSet<int>();
WillPush.ValSet.Add(1);
Stk.Push(WillPush);
int AnswerLevel = pPrevM + 1;
while (Stk.Count > 0) {
JyoutaiDef Popped = Stk.Pop();
//クリア判定
if (Popped.ValSet.Contains(pK)) {
if (Popped.Level < AnswerLevel)
AnswerLevel = Popped.Level;
continue;
}
//下限値枝切り
if (AnswerLevel <= Popped.Level + 1)
continue;
int[] wkArr = Popped.ValSet.OrderBy(X => X).ToArray();
int MaxVal = wkArr.Max();
foreach (int EachInt1 in wkArr) {
//大きい数は無駄
if (pK < EachInt1 * 2) break;
foreach (int EachInt2 in wkArr) {
if (EachInt2 < EachInt1) continue;
int NewVal = EachInt1 + EachInt2;
//大きい数は無駄
if (pK < NewVal) break;
//昇順に作成していく
if (NewVal < MaxVal) continue;
if (Popped.ValSet.Contains(NewVal))
continue;
WillPush.Level = Popped.Level + 1;
WillPush.ValSet = new HashSet<int>(Popped.ValSet);
WillPush.ValSet.Add(NewVal);
Stk.Push(WillPush);
}
}
}
return AnswerLevel;
}
}
実行結果
m(190)=10。経過時間=00:01:28.2566784
m(191)=11。経過時間=00:02:18.7473692
m(192)=8。経過時間=00:02:18.7649785
m(193)=9。経過時間=00:02:18.9376300
m(194)=9。経過時間=00:02:19.1127581
m(195)=9。経過時間=00:02:19.2877589
m(196)=9。経過時間=00:02:19.4636268
m(197)=10。経過時間=00:02:22.1859892
m(198)=9。経過時間=00:02:22.3603005
m(199)=10。経過時間=00:02:25.1153621
m(200)=9。経過時間=00:02:25.2896301
Answer=1582
解説
Kが1小さいときのm(K)の値を
深さ制限としての、深さ優先探索を繰り返してます。