球と三角形の衝突判定

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
こまいぬ

球と三角形の衝突判定

#1

投稿記事 by こまいぬ » 17年前

こんにちは
球と三角形の衝突判定について質問させてください。C言語の質問というより数学の質問に近いかもしれませんが・・・。
DirectX8.0で球と三角形の衝突判定を行うプログラムを書いたのですがうまく動きません。
なぜか斜めに衝突した場合に衝突していても衝突していない判定が返ってしまいます。
衝突判定の方法ですが、まず平面と球の中心点の距離を求め、距離が半径より小さい場合は
平面上の点で円の中心に最も近い点を求め、最後にその点で三角形の内外判定を行います。
距離を出すところまでは問題ないようなので、それ以降の部分でおかしいところがあるのだと思うのですが、どうでしょうか。
一応、周辺のソースを書いておきます。
bool ShpereTri (
	D3DXVECTOR3& c,		// 球の中心点
	float r,			 // 球の半径
	D3DXVECTOR3& t0,	// 三角形の頂点1つ目
	D3DXVECTOR3& t1,	// 三角形の頂点2つ目
	D3DXVECTOR3& t2,	// 三角形の頂点3つ目
	D3DXVECTOR3& p		// 交点収納用
	)
{
	 // 平面の法線ベクトルを求める
	 D3DXVECTOR3 n;
	 D3DXVECTOR3 v1 = t1 - t0;
	 D3DXVECTOR3 v2 = t2 - t0;
	 D3DXVec3Cross( &n, &v1, &v2 );
	 n /= D3DXVec3Length( &n );

	 // 平面と球の中心点の距離を求める
	 D3DXVECTOR3   vec = c - t0;
	 float ln = D3DXVec3Dot( &vec, &n );

	 // 距離が半径より大きい場合、衝突していない
	 if( fabsf( ln ) > r ) return false;

	 p = c - n * ln; // 平面上の、円の中心に最も近い点

	 bool point = true;

	 // 点から頂点へのベクトル
	 D3DXVECTOR3 v3 = t0 - p;
	 D3DXVECTOR3 v4 = t1 - p;
	 D3DXVECTOR3 v5 = t2 - p;

	 // 三角形の内外判定
	 D3DXVECTOR3 sm = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
	 D3DXVECTOR3 cp;

	 D3DXVec3Cross( &cp, &v3, &v4 );
	 if( D3DXVec3Dot( &sm, &cp ) < 0 ) point = false;
	 sm += cp;
	 D3DXVec3Cross( &cp, &v4, &v5 );
	 if( D3DXVec3Dot(&sm, &cp) < 0 ) point = false;
	 sm += cp;
	 D3DXVec3Cross( &cp, &v5, &v3 );
	 if(D3DXVec3Dot( &sm, &cp) < 0 ) point = false;

	 // 向きがすべて同じなので内側
	 if( point )return true;

	 return false;
}

Justy

Re:球と三角形の衝突判定

#2

投稿記事 by Justy » 17年前

>DirectX8.0で球と三角形の衝突判定を行うプログラムを書いたのですがうまく動きません
 球と三角形ですか。ちょっと複雑ですね。

 ざっと眺めただけですが、んーと。
 最後のあたりの各内積は smと cpじゃなくて、三角形の法線 nと cpでやるのでは?

管理人

Re:球と三角形の衝突判定

#3

投稿記事 by 管理人 » 17年前

力になれず、、ごめんなさい…。

mas

Re:球と三角形の衝突判定

#4

投稿記事 by mas » 17年前

ソースは未検証ですが、気になったところだけ…

> なぜか斜めに衝突した場合に衝突していても衝突していない判定が返ってしまいます。

「斜めに衝突」の意味がわかりません。
また、うまく判定できない場合の具体的な座標を教えていただけると、
回答者側としても動作を検証しやすくなるかもしれません。

> 衝突判定の方法ですが、まず平面と球の中心点の距離を求め、
> 距離が半径より小さい場合は平面上の点で円の中心に最も近い点を求め、
> 最後にその点で三角形の内外判定を行います。

最初の2点は問題なさそうですが、
「最後にその点で三角形の内外判定」の部分が条件として甘いと思います。
三角形を含む平面による球の切り口(円になります)と三角形で判定すれば
良いのではないでしょうか。
その際、
・円の中心が三角形に含まれていれば衝突
・三角形の辺が円と交差すれば衝突
と判定できます。

できます…と書きましたが、正直自信があるわけではないので、
間違っていたら、すみません。

こまいぬ

Re:球と三角形の衝突判定

#5

投稿記事 by こまいぬ » 17年前

>Justyさん
>最後のあたりの各内積は smと cpじゃなくて、三角形の法線 nと cpでやるのでは?

そうでした。ちょっと勘違いしていました。

>masさん
>三角形を含む平面による球の切り口(円になります)と三角形で判定すれば
>良いのではないでしょうか。
>その際、
>・円の中心が三角形に含まれていれば衝突
>・三角形の辺が円と交差すれば衝突
>と判定できます。

なるほど!わかりました!
言葉で表現するのが難しいので、添付ファイルで図を描いてみました。
ただ、三角形を含む平面による球の切り口で作られる円を求める方法がわかりません。
思っていたよりも難しいので、素直に線分と三角形の衝突判定で我慢したほうがいいですか?

Justy

Re:球と三角形の衝突判定

#6

投稿記事 by Justy » 17年前

 あー、なるほど。
 たしかに判定が不足しています。
 球と三角形の衝突の場合、大雑把に書くと

・ 面で接触している
・ 辺で接触している
・ 点で接触している

 という可能性があって上に書かれた 三角形の法線 nと cpとの内積の結果から判定する処理は「面で接触している」ことだけを判定しようとしています。
 なので、残りの

・ [辺] 球の中心から三角形の辺に垂直におろした点を求めてその点が辺上に含まれるかどうか
・ [点] 三角形の頂点と球の中心との距離が、球の半径以下かどうかを調べる。

を調べてみてください。

 これで正しく判定できるかと思います。

 尚、どの辺をとか、どの頂点を調べるのかは、最初の「面の接触」チェックでの結果を利用すれば正直に3頂点・3辺をチェックしなくても済むかと。



>素直に線分と三角形の衝突判定で我慢したほうがいいですか
 用途とかにもよるので一概にいえませんが、線分と三角形とかで代用しても可能であれば検討の余地はあると思います。
 処理負荷も軽くなるでしょうし。

 球と三角形とのコリジョンだと・・・キャラクタとかを地形の上を歩かせる、とかそういう用途でしょうか?

mas

Re:球と三角形の衝突判定

#7

投稿記事 by mas » 17年前

> ただ、三角形を含む平面による球の切り口で作られる円を求める方法がわかりません。

切り口で作られる円の中心は、球の中心から平面へ下ろした垂線の足で、
円の半径は、添付した図の通り球の半径と垂線の長さから求められます。

こまいぬ

Re:球と三角形の衝突判定

#8

投稿記事 by こまいぬ » 17年前

>Justyさん
>・ [辺] 球の中心から三角形の辺に垂直におろした点を求めてその点が辺上に含まれるかどうか
>・ [点] 三角形の頂点と球の中心との距離が、球の半径以下かどうかを調べる。
>
>を調べてみてください。

なるほど、わかりました。
ありがとうございます。

>用途とかにもよるので一概にいえませんが、線分と三角形とかで代用しても可能であれば検討の余地はあると思います。

キャラクターと壁の衝突判定をしようとしているのですが
線分だとキャラクターが壁に半分貫通してしまうので球を使って判定する方法を取りました。
しかしよく考えてみると、壁の衝突判定用のポリゴンをキャラクターの半径分だけ前に押し出して
描画するフィールドより一回り狭いフィールドで判定すれば線分と三角形での衝突判定でも問題なさそうです。
この方法で不都合がなければ、こっちを採用したほうがいいでしょうか?

>masさん
確かに球の半径が斜辺の長さになりますね。気づきませんでした。
どうもありがとうございます。

Justy

Re:球と三角形の衝突判定

#9

投稿記事 by Justy » 17年前

>線分だとキャラクターが壁に半分貫通してしまうので
 壁の形がシンプルなら線分でもうまくコリジョン検出できそうな気もします。
 球でやっても線分でもやっても、壁のに入ったという判定がされたら、入った分だけキャラクタの押し戻しをしていますか?


>一回り狭いフィールドで判定すれば~~こっちを採用したほうがいいでしょうか?
 専門家ではないので確かな事は言えませんが、作業効率・メモリ・処理コストなどが許すならそれでもいいかもしれません。

 ただ背景コリジョンモデルはそのキャラクタ専用というのならともかく、多分他のキャラクタとか用途でも使うでしょうから汎用的なフィールドと同等の大きさのものを使用した方がいいと思います。

 で、仮に線分を使って壁にめり込まないようにするには何本をどっちに飛ばすのかが重要になってきそうです。
 球なら大雑把でも全方向検知できますが、線分では飛ばしたその方向の情報しか判りません(壁の形状がシンプルであるとか、動的に飛ばす方向・数を決められるアルゴリズムがあるのならいいのですが)。
 
 と考えていくと、球の方がいいんじゃないかなぁ、と思います。

 あ、でもキャラクタが高速で移動して1フレームで壁を突き抜けてしまうことを検出する為に線分を使うのは有りだと思います。


 実際問題このあたりは内容とかコリジョン検出システムがどうなっているのか、モデルデータの状態とか処理負荷とかいろいろ見ながら検討しないと、確実な事を決められないのではないでしょうか。

こまいぬ

Re:球と三角形の衝突判定

#10

投稿記事 by こまいぬ » 17年前

>ただ背景コリジョンモデルはそのキャラクタ専用というのならともかく、多分他のキャラクタとか用途でも使うでしょうから
>汎用的なフィールドと同等の大きさのものを使用した方がいいと思います。

そうでした。他のキャラでも衝突判定を行う必要があることを忘れていました。やはり球の方がいいようですね。
いろいろ答えてくださってどうもありがとうございました。

いちびっと

球と三角形(線)、線と線それぞれの利点

#11

投稿記事 by いちびっと » 17年前

どもども、ゲームプログラマーの卵のいちびっとです、
黒い斜めってる線が床、青い○がキャラの半径、
赤い線がそのキャラの中心から足元までのベクトルとして
超適当に絵を描いてみました。

図を見ればキャラが床に当たっている事になっていますが、
本当に当てたい(赤いベクトルの下端)場所に当たっていません、
球と線だとどうしても、私はこの場所に当たい!と思っても無理ですが、線と線だと処理が思い…
コリジョン検出って言葉が聞きなれずよくわかりませんが…
線と線でどの程度戻すかは、線AB(キャラ)と線C(面)の交点(D)を出し(Bがはみ出てるなら)線BD分線A方向に戻せばがっつり取れますd(’’)

ゲームプログラマーになれる!って謳ってる専門学校に通っているので、当たり判定ならかなり自信がありますので、(自分でもかなり乱暴に書いた自覚があるので…)オブジェクト指向みたいに抽象的すぎて解らない!と思いましたら、もっと教えろと言ってくださいませ。<!--1

宿題で困ってます。

無題

#12

投稿記事 by 宿題で困ってます。 » 17年前

英単語を各行に1語ずつ入力し、単語が何回ずつ出現したかの頻度を出力しなさい。また、単語を辞書式順序で出力しなさい。という問題なんですが、期限が今日中なのですごくあせっています。

双方向リスト、バブルソートを使うのが条件なのですが、どのようにしたらいいでしょうか。 助けてください。

Justy

Re:無題

#13

投稿記事 by Justy » 17年前

 今日提出のものを今日掲示板で訊いてもなかなか難しいものがあります。
 細かいことも聞けないですし、そもそも双方が理解する為の時間も足りません。

 時間がないとのことなので、回答をそのまま載せます。
 ただ、入力は実行時にキーボードで入力していいのかどうか、何をもって入力の終了とするのかわからなかったので、そのあたりは適当に作ってあります。
 又、どこまで習っているのかわかりませんが、双方向リストが出てくる位なので関数・ポインタ・構造体は履修済みと考えました。
[color=#c0c0ff" face="sans-serif]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

// 単語の最大文字数
#define WORD_STRING_MAX     32

// 数値を文字列化するマクロ
#define VALUE_STR_(v)   #v
#define VALUE_STR(v)    VALUE_STR_(v)


// 単語構造体
typedef struct WordList
{
    char                word[WORD_STRING_MAX+1];
    int                 count;
    struct WordList *   next;
    struct WordList *   prev;
} WordList;

// 辞書構造体
typedef struct Dictionary
{
    int         list_num;   // 登録単語数
    WordList    *list;      // 単語リスト
} Dictionary;

// 辞書の実体
static Dictionary gDictionary;


// ----- 辞書の初期化
static void InitializeDictionary(void)
{
    gDictionary.list_num = 0;
    gDictionary.list = NULL;
}

// ----- 辞書の破棄
static void DestroyDictionary(void)
{
    WordList * curr = gDictionary.list;
    while(curr)
    {
        WordList *next = curr->next;
        free(curr);
        curr = next;
    };
}

// ---- 辞書から単語を検索する
static WordList *FindWordDictionary(const char *word)
{
    WordList *curr = NULL;

    // NULLチェック
    if(word)
    {
        curr = gDictionary.list;
        while(curr)
        {
            if(!strcmp(curr->word, word))
                break;
            curr = curr->next;
        };
    }
    return curr;
}

// ----- 辞書に単語を追加
static void InsertDictionary(const char *word)
{
    WordList *wordList;

    // NULLチェック
    if(!word)   return;

    // 既に単語があるかどうか
    wordList = FindWordDictionary(word);
    if(wordList)
        wordList->count++;
    else
    {
        // Dictionary領域確保
        wordList = (WordList*)malloc(sizeof(WordList));

        // リスト繋ぎ買え
        if(gDictionary.list)
        {
            gDictionary.list->prev = wordList;
            wordList->next = gDictionary.list;
            wordList->prev = NULL;
            gDictionary.list = wordList;
        }
        else
        {
            gDictionary.list = wordList;
            wordList->prev = wordList->next = NULL;
        }

        // 数と単語を設定
        gDictionary.list_num++;
        strcpy(wordList->word, word);
        wordList->count = 1;
    }
}

// ----- 辞書のソート(バブル)
static void SortDictionary(void)
{
    int count, loop_count;

    // 単語数が0ならスキップ
    if(gDictionary.list_num <= 0)
        return;

    loop_count = gDictionary.list_num;

    while(--loop_count)
    {
        WordList *curr = gDictionary.list;
        count = 0;
        while(curr->next && count < loop_count)
        {
            WordList *next = curr->next;
            if(strcmp(curr->word, next->word) > 0)
            {
                next->prev = curr->prev;
                curr->next = next->next;
                if(curr->prev)  curr->prev->next = next;
                if(next->next)  next->next->prev = curr;
                curr->prev = next;
                next->next = curr;
                if(gDictionary.list == curr)
                    gDictionary.list = next;
            }
            else
                curr = next;
            ++count;
        };
    }
}

// 辞書の内容を出力
static void DispDictionary(void)
{
    WordList * wordList = gDictionary.list;
    int index = 0;
    while(wordList)
    {
        printf("%4d : %s -> %2d time%c\n",
            ++index, wordList->word, wordList->count,
            wordList->count>1?'s': ' ');
        wordList = wordList->next;
    }
}
[/color]
 まだ続きます

Justy

Re:無題

#14

投稿記事 by Justy » 17年前

[color=#c0c0ff" face="sans-serif]
// ----- 単語を1つ入力し、辞書に追加する(アルファベット以外が入力されたら終了)
static int InputWord(void)
{
    char    buff[WORD_STRING_MAX];
    int     ret;

    // 単語入力
    ret = scanf("%" VALUE_STR(WORD_STRING_MAX) "s", buff);

    // 終了
    if(ret != 1 || !isalpha(buff[0]))
        return 0;

    // 辞書に追加
    InsertDictionary(buff);

    return 1;
}

// ----- 単語を複数回入力する
static void LoopInputWord(void)
{
    do
    {
        printf("Input word > ");
    }
    while(InputWord());
}

// ----- メイン関数
int main(void)
{
    // 辞書情報を初期化
    InitializeDictionary();

    // 単語を入力
    LoopInputWord();

    // 辞書をソート
    SortDictionary();

    // 出力
    DispDictionary();

    // 辞書の破棄
    DestroyDictionary();

    return EXIT_SUCCESS;
}
[/color]
 
 かなり長く、又初心者の方にはかなり難しい内容になってしまいました。

 構造体は2つ作っています。単語の情報用とその単語を集めた"辞書"の情報用です。
 全体の流れは main関数を見ればわかると思います。
 単語の入力の終了は最初の文字がアルファベット以外を入力したとき(isalpha()で判定)です。

 これで全部ですが、問題の求めるところと異なるところがありましたら、宿題で困ってます。さんの方で適当に直してみてください。


※ ポインタの繋ぎ換えなんて久々だったので、間違えていないか心配・・・

宿題で困っています。

Re:無題

#15

投稿記事 by 宿題で困っています。 » 17年前

本当にありがとうございます。
無理を言ってすいませんでした。
10時間以上いろいろ調べたり考えたりしたのですが解決できなくて。。
授業がわからなくて、パソコン教室に通おうかどうかくらい悩んでいたのですが、とりあえず印刷したり、写したりしながら考えます。
このソースのわからないところや、授業でわからないところなどこれからもちょくちょく質問しても良いでしょうか?

Justy

Re:無題

#16

投稿記事 by Justy » 17年前

>本当にありがとうございます。
 いえいえ、どういたしまして。

>悩んでいたのですが、とりあえず印刷したり、写したりしながら考えます。
 頑張ってください。

>このソースのわからないところや、授業でわからないところなど
>これからもちょくちょく質問しても良いでしょうか?

 (私から言うことではないですが)勿論良いと思います。
 ここはその為の掲示板ですから。

宿題で困ってます。

Re:無題

#17

投稿記事 by 宿題で困ってます。 » 17年前

下記は参考書に書いてあったプログラムの質問したい部分で、必要な部分だけ写したものですが、(写し間違えてたらすいません)
①~⑤の疑問があるのですが、教えていただけないでしょうか?


typedef struct Person2 {
char name[40];
int age;
struct person2 *prev;
struct person2 *next;
} Person2;

Person head, tail;

void ls_addFirst(char *nm, int ag) /*①なぜ、ここのnmには*がついているのでしょうか?山田次郎という文字はポインタということでしょうか?*/
{
Person *dt; /*②ここではPersonという構造体のポインタをとりあえず*dtにしたということでしょうか?head、tailという二つの構造体を宣言しているのに、ポインタとheadやtailというのは関係ないということでしょうか?*/
dt=psn_malloc(nm,ag);
if(head.next==NULL) tail.prev=dt;

dt->next=head.next;
head.next=dt;/*③head.nextと書くのとhead->nextと書くのはどう違いますか?*/

if(dt->next!=NUJLL) dt->next->prev=dt;
}

Person2 *psn_malloc(char *nm, int ag)/*④psn_mallocの前に*がついているのは、dtがポインタだからですよね?*/
{
person2 *p;
if((p=(person*)malloc(sizeof(Person2)))=NULL) {
printf("メモリ確保できません\n");exit(1);
strcpy(p->name, nm);/*⑤既にchar *nmでnmをポインタということにしているので、ここのnmには前に*をつけなくてもポインタということですよね?*/
p->age=ag;
p->prev=p->next=NULL;
return p;
}

int main(void)
{
head.next=tail.prev=NULL;
ls_addFirst("山田次郎",22);
return 0;
}

Justy

Re:無題

#18

投稿記事 by Justy » 17年前

>①なぜ、ここのnmには*がついているのでしょうか?
>山田次郎という文字はポインタということでしょうか?

 そうです。文字列へのポインタだからです。
 C言語では
[color=#c0c0ff" face="sans-serif]    char *name = "山田次郎";[/color]
 と書くことができます。


>②ここではPersonという構造体のポインタをとりあえず*dtにしたということでしょうか?
>head、tailという二つの構造体を宣言しているのに、ポインタとheadやtailというのは関係ないということでしょうか?

 dtは一時的に Person2の情報を扱いたい為に、ローカル変数で宣言しています。
 psn_malloc()の関数の戻り値を受け、直後の head/tailの中身を変更するために一時的に必要になった変数と考えてください。

 "dt"という名前がどこから来ているのかは判りませんが(data temporaryかも!?)、規定の範囲内なら自由に名前を付けられるので、とりあえず dtと名付けたのでしょう。


>③head.nextと書くのとhead->nextと書くのはどう違いますか?
 head->nextはコンパイルエラーになります。
 head変数はポインタ型ではありません。ポインタ型の構造体の変数がメンバ変数(prev/nextとか)にアクセスする場合は -> を使いますが、head/tailのようにポインタ型ではない実体の型の場合は .でアクセスします。

例)
[color=#c0c0ff" face="sans-serif]    Person2 A;
    Person2 *pA = &A;
    strcpy(pA->name, "山田次郎");
    printf("%s\n", [color=#e0e080">A.name[/color]);             // 実体からアクセス
    printf("%s\n", [color=#e0e080">pA->name[/color]);           // ポインタからアクセス
[/color]
 

>④psn_mallocの前に*がついているのは、dtがポインタだからですよね?
 そうですね。
 厳密にどっちが先かはともかく、psn_malloc()の戻り値の型と代入先 dtの型は一致させなければいけませんので。


>⑤既にchar *nmでnmをポインタということにしているので、
>ここのnmには前に*をつけなくてもポインタということですよね?

 そうです。 *をつけてポインタ型として宣言した変数はずっとポインタ型です。


 と、こんな感じでしょうか。

宿題で困ってます。

Re:無題

#19

投稿記事 by 宿題で困ってます。 » 17年前

なるほど。ものすごくわかりやすかったです。
Justyさんに直接お礼が言えないのが残念です。

実体とポインタの違いはだいたいわかった気がします。

例は、
Person2 *pA;
Person2 A;
pA=&A;
strcpy(A.name,"山田次郎");
と書いても同じですよね?


それと
Person2 *p;
  for(p=&head; p->next->next!=NULL; p=p->next)

のようなfor文というのは、

  Person2 tmp;
for(tmp=head; tmp.next.next!=NULL; tmp=tmp.next)

のように実体を使って書くことはできますか?
<!--2

レイトレース

行・列数可変の行列計算について

#20

投稿記事 by レイトレース » 17年前

二次元配列の分野?だと思うんですけど k行m列行列A と m行n列行列B を定義し、それらの要素をキーボードから入力し,C = A×B を計算するプログラムを作ってるんですけど授業であまり触れなかったためわかりません(1次元もあまりわかってないので・・・)
できればご指導お願いします。

box

Re:質問

#21

投稿記事 by box » 17年前

サンプルです。
#include <stdio.h>

#define K (3)
#define M (4)
#define N (2)

int main(void)
{
	int a[K][M], b[M][N], c[K][N];
	int i, j, k;
	
	/* 行列Aの要素を入力 */
	printf("%d行%d列の行列Aの要素を入力します\n", K, M);
	for (i = 0; i < K; i++) {
		for (j = 0; j < M; j++) {
			printf("%d行%d列:", i+1, j+1);
			scanf("%d", &a[j]);
		}
	}
	
	/* 行列Bの要素を入力 */
	printf("%d行%d列の行列Bの要素を入力します\n", M, N);
	for (i = 0; i < M; i++)
		for (j = 0; j < N; j++)
			printf("%d行%d列:", i+1, j+1), scanf("%d", &b[j]);
	
	/* C = A * Bの計算 */
	for (i = 0; i < K; i++)
		for (j = 0; j < N; j++)
			for (c[j] = 0, k = 0; k < M; k++)
				c[j] += a[k] * b[k][j];
	
	/* 答えの出力 */
	putchar('\n');
	for (i = 0; i < K; i++) {
		for (j = 0; j < M; j++)
			printf("%5d", a[j]);
		putchar('\n');
	}
	puts("と");
	for (i = 0; i < M; i++) {
		for (j = 0; j < N; j++)
			printf("%5d", b[j]);
		putchar('\n');
	}
	puts("の積は");
	for (i = 0; i < K; i++) {
		for (j = 0; j < N; j++)
			printf("%5d", c[j]);
		putchar('\n');
	}
	return 0;
}

box

Re:質問

#22

投稿記事 by box » 17年前

上のサンプルではK, M, Nを固定にしています。
可変にする必要がありますか?

レイトレース

Re:質問

#23

投稿記事 by レイトレース » 17年前

サンプルありがとうございます。
k、m、nは可変にする必要があるみたいです。その場合はdefineで定義できないんですよね?どうしたらいいですか?
あと,k≠m,m≠n,k≠n,k≧2,m≧2,n≧2 を満たす任意の値を設定していいみたいです。

box

Re:質問

#24

投稿記事 by box » 17年前

行数・列数を可変(実行時に与える)にした場合のサンプルです。
固定バージョンと最も異なるのは、各行列用の領域を実行時に
動的に確保する点です。
また、ついでですので、機能ごとに別々の関数にしてみました。
#include <stdio.h>
#include <stdlib.h>

void set_number_of_row_and_column(int *u, int *v, int *w);
int **allocate_area_of_array(int row, int col);
void input_data_of_array(char *name, int **a, int row, int col);
void multiple_arrays(int **c, int **a, int **b, int u, int v, int w);
void print_answer(int **c, int **a, int **b, int u, int v, int w);
void free_area_of_array(int **a, int row);

int main(void)
{
	int **a, **b, **c;
	int k, m, n;
	
	/* 行列A, Bの行数・列数を入力 */
	set_number_of_row_and_column(&k, &m, &n);
	
	/* 行列A, B, Cの領域を動的に確保 */
	a = allocate_area_of_array(k, m);
	b = allocate_area_of_array(m, n);
	c = allocate_area_of_array(k, n);
	
	/* 行列A, Bの要素を入力 */
	input_data_of_array("A", a, k, m);
	input_data_of_array("B", b, m, n);
	
	/* C = A * Bの計算 */
	multiple_arrays(c, a, b, k, m, n);
	
	/* 答えの出力 */
	print_answer(c, a, b, k, m, n);
	
	/* 動的に確保した領域の開放 */
	free_area_of_array(a, k);
	free_area_of_array(b, m);
	free_area_of_array(c, k);
	return 0;
}

void set_number_of_row_and_column(int *u, int *v, int *w)
{
	do {
		printf("行列Aの行数:"), scanf("%d", u);
	} while (*u < 2);
	
	do {
		printf("行列Aの列数(行列Bの行数):"), scanf("%d", v);
	} while (*v < 2 || *u == *v);
	
	do {
		printf("行列Bの列数:"), scanf("%d", w);
	} while (*w < 2 || *u == *w || *v == *w);
}

int **allocate_area_of_array(int row, int col)
{
	int **a, i;
	
	a = (int **) malloc(sizeof(int *) * row);
	if (a == NULL)
		fprintf(stderr, "out of memory\n"), exit(1);
	
	for (i = 0; i < row; i++) {
		a = (int *) malloc(sizeof(int) * col);
		if (a == NULL)
			fprintf(stderr, "out of memory\n"), exit(1);
	}
	return a;
}

void input_data_of_array(char *name, int **a, int row, int col)
{
	int i, j;
	
	printf("\n行列%s(%d行%d列)の要素を入力します\n", name, row, col);
	for (i = 0; i < row; i++)
		for (j = 0; j < col; j++)
			printf("%d行%d列:", i+1, j+1), scanf("%d", &a[j]);
}

void multiple_arrays(int **c, int **a, int **b, int u, int v, int w)
{
	int i, j, k;
	
	for (i = 0; i < u; i++)
		for (j = 0; j < w; j++)
			for (c[j] = 0, k = 0; k < v; k++)
				c[j] += a[k] * b[k][j];
}

void print_answer(int **c, int **a, int **b, int u, int v, int w)
{
	int i, j;
	
	putchar('\n');
	for (i = 0; i < u; i++) {
		for (j = 0; j < v; j++)
			printf("%5d", a[j]);
		putchar('\n');
	}
	puts("と");
	for (i = 0; i < v; i++) {
		for (j = 0; j < w; j++)
			printf("%5d", b[j]);
		putchar('\n');
	}
	puts("の積は");
	for (i = 0; i < u; i++) {
		for (j = 0; j < w; j++)
			printf("%10d", c[j]);
		putchar('\n');
	}
}

void free_area_of_array(int **a, int row)
{
	int i;
	
	for (i = 0; i < row; i++)
		free(a);
	free(a);
}

レイトレース

Re:質問

#25

投稿記事 by レイトレース » 17年前

問題文をよく読んでなかったみたいで・・・可変じゃなくて任意に設定して良かったみたいです。すいません。。。でも、可変のプログラムも今後のことを考えるととても参考になったと思います。ありがとうございましたm(_ _)m

閉鎖

“C言語何でも質問掲示板” へ戻る