アプリ版:「スタンプのみでお礼する」機能のリリースについて

問題 a^5 + b^5 + c^5 + d^5 = e^5 が成立する場合のa,b,c,d,eを計算するプログラムを作成したのですが、処理速度が30秒もかかってしまいます。
どこを変更、または何を追加すれば速くなるか教えてください。


条件は a < b < c < d < e
a,b,c,d,e, < 200 です。

どうかご教授お願いします。









#include <stdio.h>

int main (void)

{

int a,b,c,d,e ;

double A5,B5,C5,D5,E5;

for(a = 1; a <= 195 ; a++ )

{

A5 = a * a * a * a * (double)a ;


for(b = a+1; b <= 196 ; b++ )

{

B5 = b * b * b * b * (double)b ;

for(c = b+1; c <= 197 ; c++ )

{

C5 = c * c * c * c * (double)c ;

for(d = c+1; d <= 198 ; d++ )

{

D5 = d * d * d * d * (double)d ;

for(e = d+1; e <= 199 ; e++ )

{

E5 = e * e * e * e * (double)e ;





if((A5 + B5 + C5 + D5 ) == E5)

{

printf("a=%d,b=%d,c=%d,d=%d,e=%d\n",a,b,c,d,e);
printf("a^5=%.0lf,b^5=%.0lf,c^5=%.0lf,d^5=%.0lf,e^5=%.0lf\n",A5,B5,C5,D5,E5);

}

}
}
}
}
}

return 0;

}

A 回答 (16件中1~10件)

固定値のテーブル作成例です。

(分量多いので少し横着しました)
#define SSS(S) S(1),S(2),S(3),S(4),S(5),S(6),S(7),S(8),S( 9),S( 10),\
S( 11),S( 12),S( 13),S( 14),S( 15),S( 16),S( 17),S( 18),S( 19),S( 20),\
S( 21),S( 22),S( 23),S( 24),S( 25),S( 26),S( 27),S( 28),S( 29),S( 30),\
S( 31),S( 32),S( 33),S( 34),S( 35),S( 36),S( 37),S( 38),S( 39),S( 40),\
S( 41),S( 42),S( 43),S( 44),S( 45),S( 46),S( 47),S( 48),S( 49),S( 50),\
S( 51),S( 52),S( 53),S( 54),S( 55),S( 56),S( 57),S( 58),S( 59),S( 60),\
S( 61),S( 62),S( 63),S( 64),S( 65),S( 66),S( 67),S( 68),S( 69),S( 70),\
S( 71),S( 72),S( 73),S( 74),S( 75),S( 76),S( 77),S( 78),S( 79),S( 80),\
S( 81),S( 82),S( 83),S( 84),S( 85),S( 86),S( 87),S( 88),S( 89),S( 90),\
S( 91),S( 92),S( 93),S( 94),S( 95),S( 96),S( 97),S( 98),S( 99),S(100),\
S(101),S(102),S(103),S(104),S(105),S(106),S(107),S(108),S(109),S(110),\
S(111),S(112),S(113),S(114),S(115),S(116),S(117),S(118),S(119),S(120),\
S(121),S(122),S(123),S(124),S(125),S(126),S(127),S(128),S(129),S(130),\
S(131),S(132),S(133),S(134),S(135),S(136),S(137),S(138),S(139),S(140),\
S(141),S(142),S(143),S(144),S(145),S(146),S(147),S(148),S(149),S(150),\
S(151),S(152),S(153),S(154),S(155),S(156),S(157),S(158),S(159),S(160),\
S(161),S(162),S(163),S(164),S(165),S(166),S(167),S(168),S(169),S(170),\
S(171),S(172),S(173),S(174),S(175),S(176),S(177),S(178),S(179),S(180),\
S(181),S(182),S(183),S(184),S(185),S(186),S(187),S(188),S(189),S(190),\
S(191),S(192),S(193),S(194),S(195),S(196),S(197),S(198),S(199),S(200)

// 5乗値
#define D5D(n) (double)((double)n*(double)n*(double)n*(double)n*(double)n)
const double DIM5[201] = { (double)0,SSS(D5D) };
// 5乗値の32剰余
#define D5M32(n) ((n%32)*(n%32)*(n%32)*(n%32)*(n%32))%32
const int DIM5_MOD32[201] = { 0,SSS(D5M32) };
// 元値の32剰余
#define D1M32(n) n%32
const int DIM1_MOD32[201] = { 0,SSS(D1M32) };
// 元値の10剰余
#define D1M10(n) n%10
const int DIM1_MOD10[201] = { 0,SSS(D1M10) };
// 5乗値の10剰余(元値と同じ)
#defineDIM5_MOD10 DIM1_MOD10
    • good
    • 0

32の剰余を使えばもっと手数が少なくできそうです。


//0~31の5乗を32で割った剰余
const int DIM5_MOD32[32] = {
0,1,0,19,0,21,0,7,0,9,0,27,0,29,0,15,
0,17,0,3,0,5,0,23,0,25,0,11,0,13,0,31
};
ここから考えると、
1)5乗値の剰余が0以外の偶数になることはない。
2)5乗値の剰余が奇数なら元値の剰余は特定できる。
は適用できそうです。
5乗値の和や差の剰余値は除算しなくても元値の剰余が分かれば
上の配列を使って計算できます。
例)1^5+3^5の剰余は20になる
偶数なら剰余が0のときだけ検索すればよく、
奇数なら候補は32飛ばしで多くても6~7個です。
前述の10の剰余を使ってさらに候補を減らすこともできます。
    • good
    • 0

剰余から推測する方法は結構いけるようですね。


5乗値を10で割った剰余は元数と同じになるようです。
最下層は10飛ばしで検索できるので、
1,11,21,31,...191の5乗、
2,12,22,32,...192の5乗、
という10刻みで20個のテーブルを作っておけばいいかも。
    • good
    • 0

肝心なところを説明してなかったですが、整数でまかなえる部分は整数


演算で行うと考えれば、
a,b,c,dを決めてeを探すよりも、
a,b,c,eを決めて差分からc<d<eの範囲でdを探すほうが
演算が若干整数範囲に入りやすいと思うのです。
浮動小数プロセッサを有効活用しないと演算時間は加減算でも数倍、
乗算だと10倍以上かかるはずです。(駆使しても何倍かは重いはず)
まぁあまりいじるとこぼしそうなので正攻法でこなした方が無難かもし
れませんけど、やはり自分なら73^5を閾値にして幾分かでも整数演
算を導入したいかな。
全部浮動小数で計算するつもりなら、今まで書いた
5乗値を固定配置する(乗算をなくす)
ループの各階層で中間合計を記憶して加算演算を減らす
オーバーフローを判定(してループを抜ける)
5つ目の変数を中点検索する
くらいです。
あと些細な小技ですが、
5つ目の数は残りの4つが決まれば偶数か奇数か判別できますから、
最下層のループは逐次検索でも半分に減らせます。
5乗値の浮動小数だと判別しづらいので元数の偶奇から判別すると
いいでしょう。
    • good
    • 0

ごめんなさい。

読み違ってましたね。^^;
4個和=残りの1つですか!!
200^5も一応39ビットで基数部に入るようですね。
手持ち環境(マイコン)で試してできましたから、
(double)1*(double)1*(double)1*(double)1*(double)1,
で固定値配置はPCでも可能だと思います。
73以下なら5乗しても21億を切るので、73までの5乗配列を
用意しておき、3個和からの差分が73^5より小さい場合は整数
で判定したほうがいいです。
CPUが専用演算を持ってたとしても整数演算のほうが数倍速いの
が普通なので、手間をかけたとしてもお釣りがたっぷり来るはず。
    • good
    • 0

ループの条件式を見ると、5つの数値はそれぞれ違う数値でなければ


ならないのでしょうか?
とすれば、5乗値の配列から連続5個の和を読めば組み合わせの
上限と下限は判断つきますよね。

毎回5個和をとるのでなく、各階層で1~4個の中間和を記憶して
おけば、合計値が対象を超えた時点でブレークして次候補の演算に
すぐ移行できるはずです。
    • good
    • 0

No.2のように、予め5乗の計算結果を共通の配列に保存しておきます。


a,b,c,d,e すべての5乗の計算は共通の配列から取り出します。
処理速度が変わらないというのは無いはずです。私が検証した結果40%ほど処理速度が向上しました。

もう一つ、No.8のように枝刈りをする方法です。ソースではaから順に昇順で計算していますが、e^5を計算した後の判定において a^5 + b^5 + c^5 + d^5 < e^5 であれば、それ以降eを加算しても右辺と左辺は同値になりません。
よって、左辺 < 右辺となった時点でeのループはbreakさせることができ、ループ回数を減らせます。
この処理によりループ回数を約75%減らせます。

こちらの環境で試したところ、質問文のソースでは50秒、上記の改良後は6秒まで短縮できました。
    • good
    • 0

No.8 です。

もう一つ追加。
効果のほどはわかりませんが。

一般に、a ~ d が正の数であれば、
(a + b + c + d)^5 >= a^5 + b^5 + c^5 + d^5 です。
ですから、
(a + b + c + d)^5 < e^5 なら、a^5 + b^5 + c^5 + d^5 は、(もっと小さいので)e^5 に等しくなることはありません。
これを利用して、ループする処理の先頭で、

double sum = a + b + c + d;
sum = sum * sum * sum * sum * sum;
e5 = e * e * e * e * double(e);
if (sum < e5) continue;

という判断を入れると、ループの回数はかなり減るのではないかと思います。
これは、例えば、
1^5 + 2^5 + 3^5 + 4^5 = 100^5
のようなケースのチェックを回避するためのものです。

この判断をするための計算量とトレードオフではありますが。
    • good
    • 0

この場合、諸悪の根源は、約200回×5重のループの存在そのものです。


ループの回数を減らさないと、計算時間の向上は無理です。
例えば、
100^5 + 101^5 + 102^5 + 103^5 = 104^5
というのは、ちょっと考えれば、チェックする必要もないわけです。
実際には、(ちょっとではなく)よく考えて、チェックする必要のないものをあらかじめ除きます。

よく考えるのは苦手なので、思いつくのを2点
ループですが、
a < b < c < d < e
ですから、
for(e = 1; e <= 200; e++)
{
 for(d = 1; d < e; d++)
 {
  for(c = 1; c < d; c++)
  {
   for(b = 1; b < c; b++)
   {
 for(a = 1; a < b; a++)
    {

とネストさせることで、計算回数はかなり減らせるはずです。
また、a^5 + b ^5 の計算中に、e^5 以上になれば、それ以降チェックする必要はありません。
これは、大きい方からチェックするとすぐにあふれますから、このネストの中で、
double sum = 0
     e5 = e*e*e*e*(double)e;
     sum += d*d*d*d*(double)d;
     if (sum>= e5) continue;
     sum += c*c*c*c*(double)c;
     if (sum >= e5) continue;

のようにすれば、それなりに計算量を減らすことができるはずです。

もっとじっくり考えれば、もっと計算量を減らすことができるでしょう。
もしかしたら、(a + b + c + d) が、「何とか以上」なら、計算不要というような関係が見つかるかもしれません。
    • good
    • 0

5乗の数値はいちいち計算しなくても固定値配列で作れるはずです。


const int TBL[] = {
1*1*1*1*1,
2*2*2*2*2,
...
};
200の5乗は32ビット範囲(2^31-1まで)を超えるので
範囲内まで抑えてください。
合計値が比較対象を超えたらそれ以降の計算は無駄なのでブレーク
してください。
途中でオーバーフローする可能性も考慮必要です。
配列は右上がりなので、最後の判定部は逐次比較ではなく中点検索
でいけます。
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!