質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

ループ

ループとは、プログラミングにおいて、条件に合致している間、複数回繰り返し実行される箇所や、その制御構造を指します

Q&A

解決済

3回答

1556閲覧

C言語で1*p1^n*p2^n*p3^n (n>=0)を満たすk番目に小さい整数を求めたいです。

Tepura

総合スコア5

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

ループ

ループとは、プログラミングにおいて、条件に合致している間、複数回繰り返し実行される箇所や、その制御構造を指します

0グッド

1クリップ

投稿2020/05/09 04:21

編集2020/05/09 04:48

前提・実現したいこと

1p1^np2^n*p3^n (n>=0) (p1,p2,p3は2<=p1,p2,p3<=7を満たす素数)を満たす整数を小さい順に並べk(1<=k<=1000)番目に小さい値をできるだけ計算量を少なく求めたいです。

具体例
p1=2,p2=3,p3=5,k=4のとき)

1番目 1p1^0p2^0p3^0 = 1
2番目 1
p1^1p2^0p3^0 = 2
3番目 1p1^0p2^1p3^0 = 3
4番目 1
p1^2p2^0p3^0 = 4
5番目 1p1^0p2^0*p3^1 = 5

となり、求める値は4

発生している問題・エラーメッセージ

下記ソースコードで実行したところ900番目あたりから計算量が多くなりすぎて、CPU占有率が100%に張り付いてしまいました。

該当のソースコード

C言語

1#include <stdio.h> 2#include <string.h> 3#include <stdlib.h> 4#include <stdbool.h> 5#include <math.h> 6#pragma warning(disable : 4996) //_CRT_SECURE_NO_WARNINGS 7int main(void) { 8 int p1, p2, p3, k; 9 long long int temp, res = 2; 10 int cnt = 1; 11 long long int data[1000]; 12 data[0] = 1; 13 scanf("%d%d%d%d", &p1, &p2, &p3, &k); 14 while (cnt <= k - 1) { 15 temp = res; 16 while (res % p1 == 0) { 17 res = res / p1; 18 if (res == 1)break; 19 } 20 while (res % p2 == 0) { 21 res = res / p2; 22 if (res == 1)break; 23 } 24 while (res % p3 == 0) { 25 res = res / p3; 26 if (res == 1)break; 27 } 28 if (res == 1) { 29 data[cnt] = temp; 30 cnt++; 31 } 32 res = temp + 1; 33 } 34 if (k ==1) { 35 printf("%d\n", 1); 36 } 37 else { 38 printf("%lld\n",data[k - 1]); 39 } 40 return 0; 41}

試したこと

・上記コードではp1,p2,p3いづれの倍数にも当てはまらない数字をスキップしながら、dataに値を格納しています。

・その他考えたプログラムは3重ループを書いて1*p1^(0-n)*p2^(0-n)*p3^(0-n)のすべての組み合わせを求めた後にqsortして1000番目を求めようとしました。

しかし1000番目を求めるためにはかなり大きな配列を用意しなければいけないので、あまり有効な方法だとは思いません。

補足情報(FW/ツールのバージョンなど)

言語:C言語
OS:Windows8 64bit

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

swordone

2020/05/09 04:26

タイトルとコードでやってることが全然違うようですが?
shiracamus

2020/05/09 04:28

> 1*2^n*3^n*5^n (n>=0) を満たす整数を小さい順に並べ1000番目に小さい値 n=0 が 1番小さな値 n=1 が 2番目に小さな値 : n=999 が 1000番目に小さな値 では?
otn

2020/05/09 04:36

> 1*2^n*3^n*5^n (n>=0) を満たす整数 とコードに乖離があるのですが、もしかして、 1*2^i*3^j*5^k (i,j,kは非負の整数) を満たす整数 ですか? そもそも入力値が何者かも書いてないし。 やりたいことを正しく書いてください。
Tepura

2020/05/09 04:38

>>m.ts10806さん マークダウンでコードを再掲致しました。
Tepura

2020/05/09 04:49

>>shiracamusさん 質問内容を訂正いたしました。
Tepura

2020/05/09 04:52

>>otnさん 1*p1^n*p2^n*p3^n (p1,p2,p3は2<=p1,p2,p3<=7を満たす素数)(nはn>=0を満たす正の整数)を満たす整数のうちk番目に小さい値を求めたいです。
maisumakun

2020/05/09 06:20

C言語でなければならないですか?(C++など、他の言語は選べない状況ですか?)
otn

2020/05/09 06:34 編集

1*p1^n*p2^n*p3^n ではなく、1*p1^n1*p2^n2*p3^n3 と、べき数は異なるのでは? 1*p1^n*p2^n*p3^n とべき数が同じなら考えるまでも無く簡単。
guest

回答3

0

データ構造を活用することで計算量・メモリ量を抑えて計算することが可能です。

(p1,p2,p3)=(2,3,5)の例で行くと、1→2→3→4→5→6→8→9→… という順になっており、
指数の組に直すと次のようになります。
(0,0,0)→(1,0,0)→(0,1,0)→(2,0,0)→(0,0,1)→(1,1,0)→(3,0,0)→(0,2,0)→…

ここで、例えば(1,1,0)は必ず(1,0,0)や(0,1,0)の後、(3,0,0)は(2,0,0)の後と言うように、「指数を+1しただけ」なら、必ず「元の数より後」になりますし、ある指数の組を考えるのは「指数を-1した組の後で良い」ということになります。

つまり、最初は(0,0,0)としたら次の候補は(1,0,0),(0,1,0),(0,0,1)だけ考えれば済みますし、(1,0,0)の次は候補に更に(2,0,0),(1,1,0),(1,0,1)を追加して考える、という感じです。
シミュレートしてみると次のようになります。

1st: (0,0,0) : 次の候補**(1,0,0),(0,1,0),(0,0,1)**
2nd: (1,0,0) : 次の候補(0,1,0),(0,0,1),(2,0,0),(1,1,0),(1,0,1)
3rd: (0,1,0) : 次の候補(0,0,1),(2,0,0),(1,1,0),(1,0,1),(0,2,0),(0,1,1)
4th: (2,0,0) : 次の候補(0,0,1),(1,1,0),(1,0,1),(0,2,0),(0,1,1),(3,0,0),(2,1,0),(2,0,1)

このように考えれば、次に来る数の候補の種類を最小限に抑えつつ、小さな順に列挙できます。

この時、候補を格納する配列を「ヒープ」という、最小または最大の値を容易に取り出せるデータ構造を使うと、計算量を抑えることができます。( C++なら priority_queue が該当します )
※もっとも、k=1000 程度なら、ヒープを使わなくてもそこまで遅くはならないと思います。

なお、上記シミュレートでは重複する候補を省いて書いていますが、プログラム上で重複にどう対処するかは検討の余地があると思います。

投稿2020/05/09 07:23

編集2020/05/09 07:25
angel_p_57

総合スコア1672

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Tepura

2020/05/09 08:34

angel_p_57さん 同じくベストアンサーに選びたいくらい詳細に解説していただきありがとうございました。 ヒープを使った解法も実際に実装氏てみようと思います。 ご指南感謝いたします。
guest

0

ベストアンサー

※おそらく求められてるアルゴリズムとしては、otnさんがコメントで書いてあるものだと思うのでそれを前提にします。

まず前提としてx番目のp1^ip2^jp3^kを満たすような整数を考えると、i == j == k == 0である場合を除いて、同じようにp1,p2,p3の積で表せる数にp1かp2かp3を掛けることで作ることができます。
ということで、i == j == k == 0のパターンだけあらかじめ計算しておき、それ以降は小さいほうから順にあらかじめ計算済みのものを配列に記録していき、x番目の整数を求める場合にはそのなかからp1,p2,p3のいずれかをかけてx-1番目の整数より大きくなるもののうち最も小さいものを求めればいいだけです。
今回 k <= 1000程度なので二重ループで十分でしょう。

C

1#include <stdio.h> 2#include <string.h> 3#include <stdlib.h> 4#include <stdbool.h> 5#include <math.h> 6#pragma warning(disable : 4996) //_CRT_SECURE_NO_WARNINGS 7int main(void) { 8 int p1, p2, p3, k; 9 long long int data[1000]; 10 data[0] = 1; 11 scanf("%d%d%d%d", &p1, &p2, &p3, &k); 12 int p[3] = {p1, p2, p3}; 13 for (int i = 1; i < k; ++i) { 14 data[i] = data[i - 1] * p[0];//暫定値 15 for (int j = 0; j < i; ++j) { 16 for (int pi = 0; pi < 3; ++pi) { 17 if (data[i - 1] < data[j] * p[pi] && data[j] * p[pi] < data[i]) { 18 data[i] = data[j] * p[pi]; 19 } 20 } 21 } 22 } 23 if (k ==1) { 24 printf("%d\n", 1); 25 } 26 else { 27 printf("%lld\n",data[k - 1]); 28 } 29 return 0; 30}

※オーバーフローに関しては実際に計算して問題ない範囲であることは確認しました。
より大きなkに対しては、[0..x)の範囲が単調増加になってることを利用して二分探索を使うことも可能です。

投稿2020/05/09 06:46

yudedako67

総合スコア2047

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Tepura

2020/05/09 08:27

yudedako67さん 迅速にご回答いただき誠にありがとうございます!! 解法とソースがとても分かりやすかったのでベストアンサーにさせていただきました。 ご協力ありがとうございました!
guest

0

ご提示のプログラムを基準に考えます。

scanf("%d%d%d%d", &p1, &p2, &p3, &k);

これでパラメータ4つが決まります。そうすると条件を満たす最大の整数は(p1p2p3)^kとなります。これを仮にXと置きましょう。条件を満たす各整数はXの約数と同値になります。また、この数は(1 + k)^3通りです。これを全て計算し、整列するのがもっとも単純な方法だと思います。

もっとも、kの大きさによっては非常に大きな計算リソースを要します。(課題文中ではkでなくn)

投稿2020/05/09 06:15

HogeAnimalLover

総合スコア4830

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Tepura

2020/05/09 08:31

HogeAnimalLoverさん ご回答頂きありがとうございます。 私の質問の内容不足で誤解を招いてしまったかもしれません。 今回の問題では1*p1^i*p2^j*p3^s(i,j,s>=0)であり、同じ数で累乗するとは限らない設定でした。 ご協力頂きありがとうございました。(今後は質問の仕方に気を付けますm(__)m)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問