質問: 20*(np.random.rand(1)-0.5) *粒子同士が重ならないように配置したい -2*r < xp[m] - xp[n] < 2*r and -2*r < yp[m] - yp[n] < 2*r and -2*r < zp[m] - zp[n] < 2*r xp[m] ,yp[m] ,zp[m]は現在入れた球 xp[n] ,yp[n] ,zp[n]は過去に入れた球(1~m-1) 過去の球を取り出す方法がわかりません listを使うことは良いと思ったのですがうまくいきません 要素がランダムであることから成功しませんでした。 どうやってするか?
回答: サンプルのコードを書いてみましたので、参考にしてください。 > > > ``` > #平方根(sqrt)、累乗(pow)、乱数(random)といった関数を使うので、numpyをインポートしておく。 > import numpy as np > # 粒子間の距離を計算する関数 3次元座標(x1,y1,z1)と(x2,y2,z2)の間の距離は√((x1-x2)**2+(y1-y2)**2+(z1-z2)**2)で求まる。 > def distance3D(x1,y1,z1,x2,y2,z2): > return np.sqrt(pow((x1-x2),2)+pow((y1-y2),2)+pow((z1-z2),2)) > # 乱数で粒子の位置を出す関数 > # この質問では20*20*20の立方体に粒子を入れるので、x,y,zで共通に使う > def randPos(): > return 20*(np.random.rand(1)-0.5) > #粒子を表すクラス。インスタンス変数として粒子の位置(x,y,z)を持つ > class P: > def __init__(self,xx,yy,zz): > self.x = xx > self.y = yy > self.z = zz > # 粒子の配列。最初は空 > list=[] > # 粒子の直径。質問に明記されていなかったので、仮に直径2とした。 > d=2 > # 粒子を作る > for i in range(10): # 粒子を作るループを10回まわす > # 乱数で作った座標の粒子のオブジェクトをつくる > xp=randPos() > yp=randPos() > zp=randPos() > p = P(xp, yp, zp) > # 最初は他の粒子が無いので、作った粒子を無条件で配列に追加する。 > if i==0 : > list = list + [p] > else: > retry=5 > while retry>=0: > okFlag=True > # 既に作られている(配列listに追加されている)粒子と、新しく作った粒子の中心間距離を計算し、直径より短ければ衝突するのでフラグを降ろす > for ii in range(len(list)): > if distance3D(xp,yp,zp,list[ii].x,list[ii].y,list[ii].z) okFlag=False > if okFlag: > list = list+[p] > break > else: > retry -= 1 > if okFlag==False: > print("Fail to create a particle") > exit() # 上限回数まで粒子の作成を繰り返したが、衝突しない粒子を作れなかったので異常終了する。 > #粒子を使う > for i in range(10): > print(i, list[i].x, list[i].y, list[i].z) > > ``` > > コードの見通しが良くなるように、最初に関数とクラスを定義してあります。 "# 粒子を作る"という行から、"# 粒子を使う"という行の手前までが、互いに衝突しない粒子を作るコードです。 > > for ii in range(len(list)): > > > で始まるforループで、既に作ってある粒子と新しく作った粒子が衝突しないかどうか確認しています。 乱数で座標を決めますから、偶然粒子が他の粒子と衝突する可能性があります。 そこで、衝突した場合には何度か(コードでは最大5回)やり直しをするようにしてあります。 "#粒子を使う"という行から下が、生成した粒子の座標を使っている部分です。 コードでは、3次元座標を表示(print)しているだけですが、printのところを3次元で粒子をプロットするコードに換えれば、目的のコードになります。 === 質問者は粒子を配置するところで躓いていると思われますので、その部分を中心にコードを示しました。