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

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

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

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

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

解決済

2回答

1170閲覧

配列を扱ったc言語が変な出力で出てきて、原因がわかりません ※読みやすいようコメント多めです

renge

総合スコア18

C

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

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

0クリップ

投稿2020/08/30 12:39

編集2020/08/30 12:53

やりたいこと

1を何かで割ったときの循環節の長さを調べてます。
(ex)1÷7=0.14285714285714285...で、長さが六の142857が繰り返されるので循環節は6です。
また、便宜上割り切れる場合と循環小数にならない場合は循環節は0としてます。
pythonで作りましたが、わる数を増やしていった時に速度が足らず、対応出来なかったので高速で動くc言語で書き換えようとしました
pythonでは問題なく動作しましたがCでおかしな挙動を見せました。アルゴリズムはほぼ同じなので自分の知識不足により変な結果になっているものと思われます。
pythonも大概ですが、Cはかなりの初心者なのでお手柔らかにお願いします

##入出力
入力はinput()やscanf()などを介すことなくend_numという変数に直接入力する形をとってます
出力はpythonでは配列、cでは配列をそのまま出力できないのでpythonの配列っぽく出力させてます

わかりづらいことは実際のコードをみていただければわかると思います
##pythonでのコード

python

1import numpy as np 2count = 0  # 今何回割り算したかを記録します 3end_num = 100  # 入力です。わる数の上限です。自分の環境では100000から速度不足が深刻で計算できませんでした。 4num_list = list(np.zeros(end_num, dtype=int))  # 余りを記録するリスト。同じ余りが出てきたときが循環が一区切りついたときなので(実際に筆算をするとわかりやすいです) 5output_list = list(np.zeros(end_num+1, dtype=int)) # 出力用のリストです。 6 7 8for i in range(2, end_num + 1): 9 if i % 2 == 0 or i % 5 == 0: 10 output_list[i] = 0 # 2や5の倍数でわると必ず循環しないので0を出力のリストに入れます 11 else: 12 left = 1 % i # 余りです 13 while True: # 同じ余りが出るまでループを続けます 14 if left in num_list: 15 output_list[i] = count - num_list.index(left) 16          # 割り算した回数-今出た余りは過去に何回目で出たかで循環節が求まります。実際に筆算するとわかりやすいです 17 break 18 else: 19 num_list[count] = left 20 left *= 10 21 left %= i # でた余りを十倍してもとの数で再度割る。実際の筆算の操作と同じです 22 count += 1 23 24 count = 0 # 循環節が求まったので次の数に進むために再度初期化しておきます 25 num_list = list(np.zeros(end_num, dtype=int)) 26 27print(output_list)

出力
[0, 0, 0, 1, 0, 0, 0, 6, 0, 1, 0, 2, 0, 6, 0, 0, 0, 16, 0, 18, 0, 6, 0, 22, 0, 0, 0, 3, 0, 28, 0, 15, 0, 2, 0, 0, 0, 3, 0, 6, 0, 5, 0, 21, 0, 0, 0, 46, 0, 42, 0, 16, 0, 13, 0, 0, 0, 18, 0, 58, 0, 60, 0, 6, 0, 0, 0, 33, 0, 22, 0, 35, 0, 8, 0, 0, 0, 6, 0, 13, 0, 9, 0, 41, 0, 0, 0, 28, 0, 44, 0, 6, 0, 15, 0, 0, 0, 96, 0, 2, 0]

いくつかの数を調べてみた所実際の計算結果と一致したのでおそらく欠陥はないです。

##Cでのコード

C

1#include <stdio.h> 2 3int existinArray(int array[], int size, int element); 4/* 5 要素が存在するかを判定 6 引数: 7 int array[] 調べたい配列 8 int size 調べたい配列の要素数 9 int element 要素 10戻り値: 11 int 0か1。実質Bool型 12*/ 13int indexArray(int array[], int size, int element); 14/* 15 要素がどこに存在するかを判定 16 引数: 17 int array[] 調べたい配列 18 int size 調べたい配列の要素数 19 int element 要素 20戻り値: 21 int 要素のインデックスを表示 22*/ 23 24int main(void){ 25 size_t count = 0; //今何回割り算したかを記録します 26 int end_num = 100; //入力です。割る数の上限です。 27 int numArray[end_num]; //余りを記録する配列。同じ余りが出てきたときが循環が一区切りついたときなので(実際に筆算をするとわかりやすいです) 28 for (int i = 0; i < end_num; i++){ 29 numArray[i] = 0; 30 } 31 int outputArray[end_num+1]; //出力用の配列です。 32 for (int i = 0; i < end_num+1; i++){ 33 outputArray[i] = 0; 34 } 35 int left; //余りです 36 37 for (int i = 2; i < end_num + 1; i++){ 38 if (i % 2 == 0 || i % 5 == 0){ 39 outputArray[i] = 0; // 2や5の倍数でわると必ず循環しないので0を出力の配列に入れます 40 }else{ 41 left = 1 % i; // 余りです 42 while (1){ // 同じ余りが出るまでループを続けます 43 if (existinArray(numArray, end_num, left) == 1){ //余りが過去にでた余りと一致するかを判定 44 outputArray[i] = count - indexArray(numArray, end_num+1, left); 45           // 割り算した回数-今出た余りは過去に何回目で出たかで循環節が求まります。実際に筆算するとわかりやすいです 46 break; 47 }else{ 48 numArray[count] = left; 49 left *= 10; 50 left %= i;// でた余りを十倍してもとの数で再度割る。実際の筆算の操作と同じです 51 count ++; 52 } 53 } 54 count = 0; 55 } 56 } 57 58 // 出力部 59 printf("["); 60 for (int i = 0; i < (sizeof(outputArray)/sizeof(outputArray[0])); i++){ 61 printf("%d,", outputArray[i]); 62 } 63 printf("]"); 64} 65 66int existinArray(int array[], int size, int element){ 67 size_t output = 0; 68 for (int i = 0; i < size; i++){ 69 if (array[i] == element){ 70 output = 1; 71 break; 72 } 73 } 74 return output; 75} 76 77int indexArray(int array[], int size, int element){ 78 size_t output = 0; 79 for (int i = 0; i < size; i++){ 80 if (array[i] == element){ 81 break; 82 }else{ 83 output ++; 84 } 85 } 86 return output; 87} 88

出力
[0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,]
※最後のコンマは特に気にしなくて大丈夫です。

Ubuntu 20.04.1 LTS x86_64のターミナルでgccコマンド打ってコンパイルしてます。

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

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

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

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

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

tiitoi

2020/08/30 12:46

以下は インデックス=end_num にアクセスしてるので out of range になりませんか? int numArray[end_num]; for (int i = 0; i < end_num + 1; i++)
renge

2020/08/30 12:54

本当ですね!もともと初期化せずに使ってて、質問を書き込んでる途中に変更したので気づきませんでした。ただ、上記のように書き直して再度コンパイルして走らせてもうまく行かなかったです...
Soei

2020/08/30 16:13 編集

質問内容から外れますが、pythonの方もおかしくありませんか? 『2や5の倍数でわると必ず循環しないので0を出力の配列に入れます』とありますが、6は2の倍数で、1 / 6 = 0.16666... は循環します。
renge

2020/08/30 20:24

本当ですね。素因数が2と5のみっていうのが条件なので余りだけで判定するのは無理がありそうですね。素直に素因数分解します。
guest

回答2

0

ベストアンサー

existinArrayでチェックする範囲が間違ってませんか。

diff

1-if (existinArray(numArray, end_num, left) == 1) { //余りが過去にでた余りと一致するかを判定 2+if (existinArray(numArray, count, left) == 1) { //余りが過去にでた余りと一致するかを判定

投稿2020/08/30 15:33

編集2020/08/31 00:31
SHOMI

総合スコア4079

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

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

0

num_list = list(np.zeros(end_num, dtype=int)) に該当する処理が C 言語のほうにはないです。memset()numArray を初期化してください。

ループ内の count = 0; の上に追加

diff

1+ memset(numArray, 0, sizeof(numArray));

追記

修正依頼欄で他のSoeiさんが指定してくださってますが、i % 2 == 0 or i % 5 == 0 で循環節の長さ0とするのはおかしいですね。
反例: 1/12 = 0.08333333333
反例: 1/90 = 0.01111111111

python

1#include <stdio.h> 2#include <string.h> 3 4#define N 100 5 6/** 7 * @brief 値 value が配列 array に見つかった場合はそのインデックス、見つからなかった場合は -1 を返す。 8 * @param[in] array 配列 9 * @param[in] size 配列のサイズ 10 * @param[in] value 検索する値 11 * @return インデックス 12 */ 13int find_index(const int array[], int size, int value) 14{ 15 int i = 0; 16 while (i < size && array[i] != value) 17 ++i; 18 19 return i == size ? -1 : i; 20} 21 22/** 23 * @brief 1 / value の循環節の長さを返す。 24 * @param value 値 25 * @return 循環節の長さ 26 */ 27int func(int value) 28{ 29 int array[N] = {0}; 30 int mod = 1; 31 int i, j; 32 33 for (i = 0; i < N; ++i) { 34 mod %= value; 35 if (mod == 0) 36 return 0; // 割り切れた場合、循環節の長さは0 37 38 j = find_index(array, i, mod); 39 if (j != -1) 40 return i - j; // 過去に同じ余りがあった場合は j, j + 1, ..., i - 1 までが循環節 41 42 array[i] = mod; 43 mod *= 10; 44 } 45} 46 47int main() 48{ 49 // 1 ~ N の循環節の長さを出力する。 50 for (int i = 1; i <= N; i++) 51 printf("%d: %d\n", i, func(i)); 52} 53

投稿2020/08/30 13:17

編集2020/08/31 13:32
tiitoi

総合スコア21956

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

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

SHOMI

2020/08/30 13:30 編集

C++は{}で通りますが、C言語では{0}としないとエラーですよ。
tiitoi

2020/08/30 13:44

コメントありがとうございます。 そうなのですね。C++ でコンパイルしてました。
renge

2020/08/30 20:53

初期化の部分が足りていませんでしたね!ベストアンサー本当に迷いましたがSHOMIさんにさせていただきました。今思えばSHOMIさんの方法で問題なく動作しますけど保守性の観点から言うと初期化したほうが良さそうですね。申し訳ないです。 プログラムの方もより簡潔なコードをかいていだだけて、非常に参考になります。 コードがうまく読めなくて質問なのですが、 例えばi = 2のときってmodの値が1になると思いますけど、これはどういう流れで0になってますか?
tiitoi

2020/08/31 00:31

mod %= 2 (mod: 1) mod *= 10 (mod: 10) mod %= 2 (mod: 0) 2回目のループで0になります。
renge

2020/08/31 13:00

本当に申し訳ないんですけど、質問いいですか? ループを便宜上0回目からカウントするとおっしゃる通り1回目でmodが0になり、2回目でfind_indexで検出されて、jには1が格納されますよね?それで、funcは2-1で1を返すと思ったのですが、実際コードを回すと0が出力されてます。望んだ結果ではあるのですが、なぜこうなるのか教えて頂きたいです。 何度もお聞きしてしまい本当に申し訳ないです。
tiitoi

2020/08/31 13:15 編集

value がいくつのときの 1 / value の話をしていますか? value = 2 のときの話であれば、 0. mod (変数の中身 mod: 0) 1. mod %= 2 (1 を 2 で割ると余り1なので、変数の中身 mod: 1) 2. mod *= 10 (変数の中身 mod: 10) 3. mod %= 2 (10 を 2 で割ると余り0なので、変数の中身 mod: 0) 4. if(mod == 0) return 0; で関数は0を返して終了する という処理です。割り切れた時点でその先の find_index() に到達する前に return 0 で終了します。
tiitoi

2020/08/31 13:20

念のため確認ですが、return した時点で値を返して関数が終了するというのは大丈夫でしょうか?
renge

2020/08/31 13:23

あ、本当ですね。毎回割り切れたかどうかを判定してるんですか!自分には全くなかった発想で、コードが書いてあるにもかかわらず読み飛ばしてしまいました...本っ当に参考になりました。ご親切に何度も回答くださり本当にありがとうございました
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問