🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C

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

Q&A

解決済

3回答

6090閲覧

c言語で2種類の配列で、数字がどちらにも入っているものと、どちらかに入っているもの(積集合、和集合)を表示したいです

tsuyoku-naritai

総合スコア7

C

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

0グッド

0クリップ

投稿2021/01/29 08:03

編集2021/01/29 08:09

C言語で以下のプログラムを作りたいです。

大きさが10の整数型の配列を2つ作り、それぞれに1から10の乱数を代入して、共通する数とどちらかにある数をそれぞれ昇順に表示させる。

例えば
配列1:5 8 4 3 7 9 7 9 5 1
配列2:7 6 1 5 9 9 10 8 4 3
共通する数:1 3 4 5 7 8 9
どちらかにある数:1 3 4 5 6 7 8 9 10

自分はまず乱数を発生させて、それぞれの配列で複数回ある数を除き、それから&&や||を使って求めて、昇順に並べようと思いましたが、配列で複数回ある数を除く方法と昇順に並べる方法がわかりませんでした。
なので、その複数回ある数を除く事と昇順に並べる事の方法や考え方など教えてください。
ちなみに、配列で複数回ある数を除くとは
配列1:5 8 4 3 7 9 7 9 5 1では7が2回出てきているので1つにするという事です。

また、別の方法が良ければそれも教えて欲しいです。

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

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

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

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

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

guest

回答3

0

ビット演算を学習しましょう。

C

1#include <stdio.h> 2 3#define N 10 // 配列の要素数 4#define A 1 // 要素の値の範囲 A以上 B以下 5#define B 10 6 7void print(int b) 8{ 9 for (int i = A; i <= B; i++) 10 if (b >> i & 1) printf(" %d", i); 11 putchar('\n'); 12} 13 14int main(void) 15{ 16 int a1[N] = { 5, 8, 4, 3, 7, 9, 7, 9, 5, 1 }; 17 int a2[N] = { 7, 6, 1, 5, 9, 9, 10, 8, 4, 3 }; 18 int b1 = 0, b2 = 0; 19 for (int i = 0; i < N; i++) b1 |= 1 << a1[i], b2 |= 1 << a2[i]; 20 printf("共通する数:"), print(b1 & b2); 21 printf("どちらかにある数:"), print(b1 | b2); 22}

追記
ビット演算を使わないやり方です。

C

1#include <stdio.h> 2 3#define N 10 // 配列の要素数 4#define A 1 // 要素の値の範囲 A以上 B以下 5#define B 10 6 7void print(int b[B+1]) 8{ 9 for (int i = A; i <= B; i++) 10 if (b[i]) printf(" %d", i); 11 putchar('\n'); 12} 13 14int main(void) 15{ 16 int a1[N] = { 5, 8, 4, 3, 7, 9, 7, 9, 5, 1 }; 17 int a2[N] = { 7, 6, 1, 5, 9, 9, 10, 8, 4, 3 }; 18 int b1[B+1] = { 0 }, b2[B+1] = { 0 }, c[B+1] = { 0 }, d[B+1] = { 0 }; 19 for (int i = 0; i < N; i++) b1[a1[i]] = b2[a2[i]] = 1; 20 for (int i = A; i <= B; i++) { 21 c[i] = b1[i] * b2[i]; // 積集合 22 d[i] = b1[i] + b2[i]; // 和集合 23 } 24 printf("共通する数:"), print(c); 25 printf("どちらかにある数:"), print(d); 26}

ビット演算を使わなければ理解できるのでしょうか?
コメントをお願いします。

追記2
ソートした 2つの配列を先頭から見ていって、積集合と和集合を求めるやり方。

C

1#include <stdio.h> 2 3#define N 10 // 配列の要素数 4#define A 1 // 要素の値の範囲 A以上 B以下 5#define B 10 6 7void print(int a[], int n) 8{ 9 for (int i = 0; i < n; i++) 10 printf(" %d", a[i]); 11 putchar('\n'); 12} 13 14void sort(int a[]) 15{ 16 for (int i = 0; i < N-1; i++) 17 for (int j = i+1; j < N; j++) 18 if (a[i] > a[j]) { 19 int t = a[i]; a[i] = a[j]; a[j] = t; 20 } 21} 22 23int intersection(int a1[], int a2[], int b[]) 24{ 25 int i = 0, j = 0, k = 0, t = A-1; 26 while (i < N && j < N) 27 if (a1[i] < a2[j]) 28 i++; 29 else if (a1[i] > a2[j]) 30 j++; 31 else { 32 if (a1[i] != t) b[k++] = t = a1[i]; 33 i++, j++; 34 } 35 return k; 36} 37 38int union_set(int a1[], int a2[], int b[]) 39{ 40 int i = 0, j = 0, k = 0, t = A-1; 41 while (i < N && j < N) 42 if (a1[i] < a2[j]) { 43 if (a1[i] != t) b[k++] = t = a1[i]; 44 i++; 45 } 46 else { 47 if (a2[j] != t) b[k++] = t = a2[j]; 48 j++; 49 } 50 for (; i < N; i++) 51 if (a1[i] != t) b[k++] = t = a1[i]; 52 for (; j < N; j++) 53 if (a2[j] != t) b[k++] = t = a2[j]; 54 return k; 55} 56 57int main(void) 58{ 59 int a1[N] = { 5, 8, 4, 3, 7, 9, 7, 9, 5, 1 }; 60 int a2[N] = { 7, 6, 1, 5, 9, 9, 10, 8, 4, 3 }; 61 printf("配列1:"), print(a1, N); 62 printf("配列2:"), print(a2, N); 63 sort(a1); 64 printf("配列1(昇順):"), print(a1, N); 65 sort(a2); 66 printf("配列2(昇順):"), print(a2, N); 67 int b[B-A+1], n; 68 n = intersection(a1, a2, b); 69 printf("共通する数:"), print(b, n); 70 n = union_set(a1, a2, b); 71 printf("どちらかにある数:"), print(b, n); 72}

実行結果

text

1配列1: 5 8 4 3 7 9 7 9 5 1 2配列2: 7 6 1 5 9 9 10 8 4 3 3配列1(昇順): 1 3 4 5 5 7 7 8 9 9 4配列2(昇順): 1 3 4 5 6 7 8 9 9 10 5共通する数: 1 3 4 5 7 8 9 6どちらかにある数: 1 3 4 5 6 7 8 9 10

投稿2021/01/29 09:51

編集2021/02/05 09:49
kazuma-s

総合スコア8224

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

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

tsuyoku-naritai

2021/01/30 10:00

回答ありがとうございます。ビット演算を学習してから参考にしてもう一回作ってみます。
tsuyoku-naritai

2021/02/01 10:23

返信遅れてすいません。#difineが何なのかはわかりませんでした。でも別のやり方で解決しました。ありがとうございました。
guest

0

ベストアンサー

複数回あるのを除くには、ソートしてから新しい配列を用意して、元の配列の小さい方から順に、数値が大きくなったときだけ、新しい配列に数値を入れていけばいいです。
「2つの配列を1つにまとめる→ソート→重複を除く」で和集合はできます。
積集合は「2つの配列をそれぞれソート→小さい方から比べつつ共通の数値があったら新しい配列に入れる」というような動作になります。添字の進め方に工夫が必要なので考えてみてください。

また、1~10ということがわかっているなら、ビット演算を用いることでもっと楽にできます。
0に対して元の配列から1<<(数値)との論理和を取っていけば、たとえば{1,2,3,5,5,5,8,8,9,9}なら2進数で1100101110になるので、2つの配列をこのようにint型の数値に変換してからビット論理和、論理積をそれぞれとって配列にもどせば終わりです。


追記
回答修正が迷走してしまってすみません。
「重複を消す」の例はこんな感じですね

c

1int main(void) { 2 int ab[20] = {1,1,2,3,3,4,5,6,7,7,7,7,7,8,8,9,9,9,9,10}; // aとbを連結してソートした配列 3 int or[10]; 4 or[0] = ab[0]; 5 for (int i=0,j=0;i<20;i++) { 6 if (ab[i] > or[j]) or[++j] = ab[i]; 7 } 8}

投稿2021/01/29 08:29

編集2021/01/30 11:37
kairi003

総合スコア1332

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

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

tsuyoku-naritai

2021/01/30 10:14 編集

回答ありがとうございます。 複数回あるのを除くところまでやってみたのですが、数値が大きくなった時だけ、新しい配列に入れるところがうまくいきません。 ```C #include <stdio.h> #include <time.h> #include <stdlib.h> int main(void){ int a[10],a2[10],b[10],b2[10],i,j,x; srand((unsigned)time(NULL)); printf("配列1:"); for(i = 0;i < 10;i++){ a[i] = rand() % 10 + 1; printf("%d ",a[i]); } printf("\n配列2:"); for(i = 0;i < 10;i++){ b[i] = rand() % 10 + 1; printf("%d ",b[i]); } for(i = 0;i < 10;i++){ for(j = i+1;j < 10;j++){ if(a[i]>a[j]){ x = a[i]; a[i] = a[j]; a[j] = x; } } } for(i = 0;i < 10;i++){ for(j = i+1;j < 10;j++){ if(b[i]>b[j]){ x = b[i]; b[i] = b[j]; b[j] = x; } } } printf("\n"); for(i = 0;i < 10;i++){ printf("%d ",a[i]); } printf("\n"); for(i = 0;i < 10;i++){ printf("%d ",b[i]); } printf("\n"); for(i = 0;i < 10;i++){ for(j = 0;j < 10;j++){ if(a[j]<a[j+1]){ a2[j] = a[i]; } } } for(i = 0;i < 10;i++){ for(j = 0;j < 10;j++){ if(b[j]<b[j+1]){ b2[j] = b[i]; } } } for(i = 0;i < 10;i++){ printf("%d ",a2[i]); } printf("\n"); for(i = 0;i < 10;i++){ printf("%d ",b2[i]); } printf("\n"); } ``` (途中のprintfは確認用です) これが自分で考えてみたものなのですが、何かアドバイスいただけないでしょうか?
tsuyoku-naritai

2021/02/01 10:25

うまくいきました。ありがとうございます。
guest

0

それぞれに1から10の乱数を代入

ということで,値の種類が少ないので,
1~10 のそれぞれの値が,{配列1にあるか?,配列2にあるか?}を調べるのが手っ取り早いように思います.

C

1//1~10の値が,{配列1にあるか?,配列2にあるか?}を調べた結果を入れる用. 2//各 CheckFlag[x] はbitフラグの組み合わせとして, 3//配列1にある場合は.0x01 を,配列2にある場合は0x02 をORする形で結果を作るとか,そんな感じで. 4unsigned char CheckFlag[10] = { 0,0,0,0,0, 0,0,0,0,0 };

こんな感じで.

C++

1#define ARRAY1_SIZE 10 2#define ARRAY2_SIZE 10 3 4int main(void) 5{ 6 //配列1と配列2 7 const int Array1[ARRAY1_SIZE] = { 5, 8, 4, 3, 7, 9, 7, 9, 5, 1 }; 8 const int Array2[ARRAY2_SIZE] = { 7, 6, 1, 5, 9, 9, 10, 8, 4, 3 }; 9 10 //調べる処理 11 unsigned char CheckFlag[10] = { 0,0,0,0,0, 0,0,0,0,0 }; 12 for( int i=0; i<ARRAY1_SIZE; ++i ){ CheckFlag[ Array1[i]-1 ] |= 0x01; } 13 for( int i=0; i<ARRAY2_SIZE; ++i ){ CheckFlag[ Array2[i]-1 ] |= 0x02; } 14 15 //結果表示 16 printf( "共通 : " ); 17 for( int i=0; i<10; ++i ) 18 { 19 if( CheckFlag[i]==0x03 ){ printf( "%d ", i+1 ); } 20 } 21 printf( "\n" ); 22 23 printf( "どっちかにある : " ); 24 for( int i=0; i<10; ++i ) 25 { 26 if( CheckFlag[i] ){ printf( "%d ", i+1 ); } 27 } 28 printf( "\n" ); 29 30 return 0; 31}

「bitがどうの」的な話が嫌なら,
配列1の調査結果と配列2の調査結果を別個に持てばいい.
調査結果データの表現方法が異なるだけで,やってることは一緒.

C++

1//調べる処理 2unsigned char CheckResult1[10] = { 0,0,0,0,0, 0,0,0,0,0 }; 3unsigned char CheckResult2[10] = { 0,0,0,0,0, 0,0,0,0,0 }; 4for( int i=0; i<ARRAY1_SIZE; ++i ){ CheckResult1[ Array1[i]-1 ] = 1; } 5for( int i=0; i<ARRAY2_SIZE; ++i ){ CheckResult2[ Array2[i]-1 ] = 1; } 6 7//結果表示 8printf( "共通 : " ); 9for( int i=0; i<10; ++i ) 10{ 11 if( CheckResult1[i] && CheckResult2[i] ){ printf( "%d ", i+1 ); } 12} 13printf( "\n" ); 14 15printf( "どっちかにある : " ); 16for( int i=0; i<10; ++i ) 17{ 18 if( CheckResult1[i] || CheckResult2[i] ){ printf( "%d ", i+1 ); } 19} 20printf( "\n" );

値の種類が少ないことを利用すれば,「重複を除去して昇順に」の話も同じ考えでやれる.

C++

1#define ARRAY_SIZE 10 2 3int main(void) 4{ 5 //元の配列 6 const int SrcArray[ARRAY_SIZE] = { 5, 8, 4, 3, 7, 9, 7, 9, 5, 1 }; 7 //結果配列用 8 int ResultArraySize = 0; 9 int ResultArray[ARRAY_SIZE] = { 0,0,0,0,0, 0,0,0,0,0 }; 10 11 //1~10の各値が元の配列に存在するか調べる 12 unsigned char CheckResult[10] = { 0,0,0,0,0, 0,0,0,0,0 }; 13 for( int i=0; i<ARRAY_SIZE; ++i ){ CheckResult[ SrcArray[i]-1 ] = 1; } 14 //調べた結果から,結果配列を作る 15 for( int i=0; i<10; ++i ) 16 { 17 if( CheckResult[i] ){ ResultArray[ ResultArraySize++ ] = i+1; } 18 } 19 20 //結果表示 21 printf( "重複を取り除いてソートした結果(%d 個になった)\n", ResultArraySize ); 22 for( int i=0; i<ResultArraySize; ++i ){ printf( "%d ", ResultArray[i] ); } 23 printf( "\n" ); 24 25 return 0; 26}

投稿2021/01/29 08:11

編集2021/01/29 08:59
fana

総合スコア11990

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

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

tsuyoku-naritai

2021/01/29 08:23

すいません、まだC言語をはじめて間もないので、bitフラグの組み合わせが何なのかわかりません。なので、できればbitフラグというものを使わない方法を知りたいです。
fana

2021/01/29 08:32

コード例を追加した. 要は,1~10 の各値に関して, ・どちらの配列にも無い場合は 0 ・配列1にだけある場合は 1 ・配列2にだけある場合は 2 ・どちらにもある場合は 1+2 = 3 という形で,調査結果情報を作る.それだけの話.
fana

2021/01/29 08:40

「bitがどうの」という話が慣れないなら, 1~10 の各値が ・配列1にあるかどうか ・配列2にあるかどうか という別個の調査結果を作ってやる形にすれば良いかと.
tsuyoku-naritai

2021/01/30 09:57

回答ありがとうございます。自分はまだ知識不足で十分に理解できませんでした。何度も回答してくださったのに、すいません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問