カテゴリー: テクノロジー

最短ルートを探せる”A* パスファインディング”を試してみた

こんにちは。開発担当のマットです。
Sploutでは様々なテクノロジーを使って色々開発していますが、その中でも特にゲーム・プログラミングが1番好きです。
 
日本ではあまり知られていませんが、小さい時からシヴィライゼーションというゲームが大好きでした。

civgame
『シヴィライゼーション 2』のスクリーンショット

ルールは複雑ですが、目的は単純です。
国のリーダーとして、都市を設立して、軍隊を作り、敵を倒し、世界の征服者になることです。
 
プログラムを書けるようになった頃、このようなゲームを作成することをすぐ試してみました。
1人の人間のプレーヤー対複数のコンピュータ・プレーヤーのゲームにするつもりだったので、世界のマップを作って将棋盤のようにそれをマスに分けて(海のマス、森林のマス、野原のマス、山のマス、など)、将棋の駒のような軍隊のピースを設置しました。
歩兵部隊、戦車、軍艦など、いろいろな種類を用意して通過可能と通過不可能のマスも定義しました。
 
歩兵部隊の場合、海と山が通過できない。
戦車の場合、それプラス森林もダメ。
軍艦は海のみ通過できるなど。
 
一マスずつ動かす場合、コードはとてもシンプルでした。
隣のマスはその駒にとって通過可能であれば、通過を許す。
一マスずつしか動かないので、遠い目的地までのルートは頭の中で管理していました。
 
ところがコンピュータの番になると目的地までのルート管理で困りました。
駒と目的地の間に山や海やそのような通過不可能なマスがあったら、その障害を避ける方法を見つけなければなりませんが、実際にどうやってするのでしょう?

「パスファインディング」とは?

名前の通り、「パスファインディング」は「パス(道)ファインディング(探し)」のことです。
つまり、「パスファインディング」は A という出発地から B という目的地までの道を見つける方式のこと。
 
様々なパスファインディング方法がありますが、この記事で紹介するのは1番多く使われている「A* パスファインディング」という方式です。

A* パスファインディング方式の実例

単純に、A*(エイ・スター)とも呼ばれますが、この方式を正しく使うと、必ず1番短い道を見つけることができます。
少し複雑なので、実際の例で説明したいと思います。

*草色は通過可能、青色は通過不可能とします。

 
この実例で、このマップのA出発池からB目的地までの一番短い道を探します。
コーディングを始める前には、以下の基本知識が必要となります。
 
1.パスファインディングの用語で「マス」は「ノード」と言うので、以下では「ノード」と呼ぶ。
 
2.この実例では、1ノード=1m(メートル)。即ち、上下左右を動く場合、距離は 1.0mとなる。
 
3.しかし、斜めに行く場合、「√2.0m」となります。実際にそれを使うと処理が遅くなる上、実例が複雑になりますので、端数を切り捨てて、1.4mとする。
 
4.それぞれのノードは以下の変数を持つ。
>  g (これは「今まで辿ってきた道の距離」を持つ変数)
>  h (これは「直線で目的地までの距離」を持つ変数)
>  p (これは「このノードに着くまでの手前のノード」を指す変数)
 
5.確認できるノードを「open」というリストに保管する。
 
6.既に確認したノードは「closed」というリストに保管される。
>  一旦closedに入ると、openに戻されることはない。
 
7.その時、調べているノードは「current」とされる

A*パスファインディングの手順

1.まず、A出発地を「current」と指定する。
 
2.currentが目的地であれば終了。
 
3.以下の条件を満たす場合、currentに接触しているノードをopenリストに追加する。
〈条件〉
接触しているノードは通過可能である
接触しているノードはclosedリストに含まれていない
 
4.currentに接触しているノードの g と h を計算し以下のルールを従う。
 
ルール①
もし、そのノードのg,h,p値がまだ設定されていない場合、そのノードのgとhを設定しそのノードのpをcurrentと設定する。
 
ルール②
もし、そのノードのg,h,p値が既に設定されている場合、新しく計算したgは既に設定されているgより少ない場合のみ新しく計算したgを設定し、そのノードのpをcurrentに設定する。
 
5.currentをclosedリストに追加する。
 
6.openリストの中で、g+hが一番低いノードをcurrentと設定する。
 
7.ステップ2からステップ7まで繰り返す。

実例

上記の手順だけではわかりにくいので、実際の例で上記の手順を使って一番短い道を見つけましょう。
 
まず、A出発地を「current」と定義する。

黄色=current

 
次、currentと接触しているノードをopenリストに追加する。
a2
薄オレンジ=openリスト

 
次、それぞれの g と h 値を計算し設定する。
なお、それぞれのpはcurrentとなります。
以下の図では、pを矢印で表す。
(それぞれのノードのpノードは差し先です)
a3
g = 来た距離、 h=直線で目的地まで

一旦、図を見てみましょう。
 
例として、C3にあるノードを見てみましょう。
C3のG値は1.4  出発地からC3まで、1.4m分の道を行かなければならないからです。
C3のH値は5.4  C3から目的地まで直線でいけたら、5.4mはまだあるからです。
(4つ右、1つ斜めので、4*1.0m + 1*1.4m = 5.4m)
なお、C3のpはまだ設定されていなかったので、pはcurrentのB4と設定されました。(左下向きの矢印で表している)
 
次はステップ5とステップ6。
current(B4)をclosedに追加し、openリストの中のgとhの合算が一番低いノードは新しいcurrentになります。
今の時点では、それはC4となります。
(C4の gとhの合算(5.0+1.0)は他のopenノードよりも低いため)
a4
次はC4を調べる

 
次はcurrent (C4)に接触しているノードをopenに追加する。
(ご注意:B4はclosedに含まれていて、D3は通過不可能ので、いずれも追加しない)
a5
接触している、且つopenにあるものは紫枠に

 
次、上記の図で紫枠になっているノードの g と hを計算します。
D4とD5は何も入っていないので入れるだけで問題ないですが、既に設定されているものも確認しなければなりません。
 
例として、C5を見てみましょう。
C5がB4から直接来た場合、gは1.4mになりますが、C4から来た場合2.0mになります
(B4→C4の1.om + C4→C5の1.0m)
 
既に設定された1.4mの方が短い道なので、C5のp(手前のノード)は出発地のB4のままでいいです。
なお、gを上書きしません。
計算が全部終わると、このようになります。
a6a
C4の接触ノードを調べ終わった後

 
次はステップ5とステップ6。
確認済みのノードをclosedに追加して、g+hが一番低いノードをcurrentとして設定する。
gとhの合算が一番低いノードはD4です。
a7b
次はD4を調べる

 
次はD4と接触しているノードをopenに追加し、gとhを計算する。
a8
紫色はチェック対象となるノード

 
ご覧の通りE3,E4は設定されていなかったので設定されて、pがcurrentのD4となりました。
しかし、D4経由でC3,D5,C5まで行くとそれぞれのg値がより高くなってしまうので上書きされません。
(例えば、D4からD5に行く場合、gは3.0mになるが、C4からD5に行く場合、gは2.4mので、D5のpはC4のままでいいです)

 

次、各openノードを比較してgとhの合算が一番低いノード(E4)をcurrentにしD4をclosedリストに移す。

a9
一つずつ順番にやっていきます

 
次は、E4と接触しているノードをopenして、gとhを計算する。
(今回、接触ノードは全て通過不可能、または既にopenに追加されているので、openリストに何も追加しなくていいが、E3とD5のE4から辿った場合のgを計算する必要があります。今回、どれも上書きされません。)
 
E4をclosedにし、gとhの合算が一番低いノードをcurrentに切り替える
しかし各openノードのgとhの合算を確認したら、E4もD5もC3もC5も同じ6.8mとなっています。
 
その場合、どちらを選んでも問題ないです。
今回の実例では、h値が一番低いものを優先に調べることにしましょう。
(結果的に、A*は一番短い道を確実に見つけてくれるけど、複数の等しい道がある場合、この「どれを優先に調べる」ルールはその等しい道の中、どの道になるかを影響する。優先順位をランダムで決定することでも問題ありません。)
 
E4、D5、C3、C5の中、
h値が一番低いのはE3ので、それをcurrentにします。
a10
行き止まりであるE3も確認する

 

E3の接触ノードは全て通過不可能、またはclosedに入っている。
残念ながら、行き止まりでした。
g、hを計算できる接触ノードはありませんので、openリストを広げるステップをスキップし、E3をclosedに入れる。
 
まだC3,C5,D5、三つとも、6.8mとなっていますが、h値が低いのはD5ので、
それをcurrentに切り替える
 
そのD5の接触ノードを確認して、gとhの計算をする。
a11
 
この手順を繰り返すと目的地に近づいていきます。
a12
 
a13
 
a14
 
やっと、目的地のH4はopenリストに追加され、gとhが計算されました。
次のステップで目的地がcurrentになります。
 
そうなるとこの手順の繰り返しを完了します。
a15
 
ちなみに目的地に着かずopenリストを尽くしてしまう場合もあります。
その場合「道が存在しない」という意味になるのでコードを書く時は、その可能性も考え対応する必要があります。
 
やっと目的地に辿り着いて一番短い道の距離は7.8mであることがわかりました。
目的地からp値で遡ると道のルートも取得できます。
目的地H4のpはG5.
G5のpはF6.
F6のpはE6.
E6のpはD5.
D5のpはC4.
C4のpは出発地のB4.
a16
 
つまり、行く道はその反対
B4→C4→D5→E6→F6→G5→H4
a17

A* パスファインディングの特徴

上記の実例はとても単純なマップでしたが、複雑な迷路のようなマップでも1番短い道を必ず見つけてくれることが確実です。
a18
 
なおgとhの合算が低いものから調べていくため、他のパスファインディング方式より処理がより速いです。

ウェイティング(重み付け)

なお、ウェイティングをすると、通過可能の障害物もシミュレートできます。
以下のマップを見てみましょう。
a19

 
この実例では人と馬が目的地に行きたいですが、間に森林がある。
このゲームでは人は構いなく森林を通ることができるけど、馬は森林を避ける傾向を持つ(でも、仕方がない場合、通る)。
 
それをシミュレートするには、gを計算するとき何らかの重み付けを使います。
 
つまり、人がまっすぐ森林のノードに移ると+1.0と計算するが、
馬の場合、野原は森林より5倍好ましければ馬がまっすぐ森林のノードに移ると+5.0と計算する。
(hは通常通り、直線距離で計算する)
 
これをすると、人は割りとまっすぐなパスを辿る。
馬はできるだけ分厚い森林を避けようとする。
a20
 
同様に、反対のこともできます。
「障害物」を避けるために重み付けをするだけではなく道路などを優先的に使うよう野原に重み付けをして、道路に重付けをしないこともできる。
a21
 
***ご注意
重み付けをする際、1.0より小さい数字を使うと「必ず一番短い道を見つける」特徴がなくなります。
その反面大きいな数字を使うとあまりにも避けようとしすぎてコードの処理が遅くなります。

まとめ

『もしアップルパイを最初から作りたいと思ったら、まずは宇宙を創造しなければなりません』- Carl Sagan
 
マップ上で駒を動かすのはものすごく単純な行為には見えますが、プログラミングの世界では単純なことは中々ありません。
 
コンピュータにとっては簡単ですが、私は 312198 * 1912 の暗算はできません。
その反面、散歩をするときは自然に目的地まで歩くことができるのに、コンピュータにとっては自然ではないので、A*などの方式でコードを書いて教えなければなりません。
 
今回の記事で紹介しました方式は主に、ゲーム・プログラミングやロボティクスで活用できますが、このような問題を1つ1つ解決していくことがプログラマーの1番の楽しみと思います。
 
自分に挑戦したいと思う方は、ぜひ上記のA*パスファインディング方式を実際に使って簡単なプログラムでも書いてみるのはいかがでしょう。

ウェブサービスまたはスマートフォンアプリの国際対応のススメ

こんにちは。開発担当のマットです。
現在、私はスプラウトで様々な開発をしていますが、開発者になる前は日英翻訳の仕事をしていました。
 
翻訳は面白いものだと思います。
文章を訳すことによって言語の壁を超えることができます。
また、いい翻訳者であれば異なる文化を持つ多くの国の方々に著者の感情と強調まできちんと届けることができます。

小説の翻訳フローはだいたい同じです。
日本語で小説を書いて出版する。
海外から需要がある場合、翻訳会社に委託して翻訳が出来上がったら海外にも出版する。

でも、ソフトウェアの場合、翻訳はそれほど簡単ではありません。
本記事で、一般的なウェブサービスまたはスマートフォンアプリなどの国際化の利点、およびチャレンジについて書きたいと思います。

 

ソフトウェア国際化の利点
「日本語だけでいいんじゃないの?」

市場として、日本は小さくありません。
この記事を書く時点(2016年12月)では、1億2620万人の日本人がいます。
なお、日本のGDPは500兆円を超えています。

けれども、世界の割合として日本人は世界人口の2パーセントにも達していません。
なお、世界GDPの6パーセントです。

ウェブサービスまたはアプリなどを開発する際、日本にいない98%の人間と94%の市場を見過ごしてしまうとすごくもったいないです。

人口の比較。日本は世界人口の2%も占めません。
人口の比較。日本は世界人口の2%も占めません。

世界規模なサービスを開発するにあたってのチャレンジ
「いったいどこから始まる?」

「サービスを国際化する」イコール「サービスを翻訳する」と思いがちですが、それほど簡単ではありません。

サービスの国際対応は大きく3つの段階に分けることができます。

  1. Multilingualization (多言語化)
  2. Localization (現地化)
  3. Internationalization (国際化)

Multilingualization (多言語化)

atoa

国際対応にあたって、多言語化は一番想像しやすい項目だと思います。

「Multilingualization(多言語化)」とは、サービスを他の言語で表示できるようにすることです。

ところが、なんでも簡単に訳すことができません。
特に気にしないといけないのは、動的文言です。
文章の中に変数が入る場合、色々な問題が発生します。

私の経験上で、一番悩んできた問題を以下にまとめます。

 

単数・複数の区別

「このユーザは{X}人の友達がいます」という文章は簡単に見えますが、英語に訳すと、一人の場合は「friend」、一人以上の場合は「friends」になります。
サービスを多言語化する際、翻訳文の管理システムに、単数・複数の区別ができるように作らないといけません。

なお、英語の場合は、1 = 単数、1ではない = 複数、ですが、フランス語の場合、0 も単数扱いという文法ルールが存在します。
なお、ロシア語の場合、21,31,41などが単数扱いで、100,1000,10000などは全く別の「大複数」的な扱いになります。

要するに、言語によって単数・複数の区別は非常に複雑で、翻訳者がその区別を入力できるよう多言語システムを作るべきです。

 

文法的性

日本語に全くない概念ですが、単語に性別が属する言語も存在します。
「お爺さん、お婆さん」を英語に訳すと単純に「the grandfather, the grandmother」になりますが、スペイン語だと、「el abuelo, la abuela」になってしまいます。

なお、性別を持っていそうな「お爺さん、お婆さん」だけではなく、性別を持っていなさそうな単語も同様です。
例えば、「皿」は男性用のelを使う。ギターは女性用のlaを使うそうです。

同じような現象ですが、日本語では性別で区別しないが、生物・非生物の使い分けがあります。
「動物がいる。物がある。」などで見られます。

これは簡単に対応できないですが、なるべく翻訳者がこのような区別を入力できるよう多言語システムを作れたら作るべきです。

(しかし、私の経験上でほとんどの翻訳者はこの問題を回避するよう、文章を上手に書くことができます)

 

UIが言葉の順番に依存すると困ります。

時々出てくる問題ですが、文章の中の単語の順番が変わるとUIが崩れてしまうことがあります。

このようなUIの多言語化作業が非常に大変になってしまいます。
なので、絶対にしてはいけないこととは言えませんが、開発者様はこの事実を考慮しておくと後の悩みを防ぐことができます。

Localization (現地化)

globe_flags

多言語化の段階で言語の壁を超えることができても、国境はまだ超えていません。

「Localization (現地化)」とは、日本と異なる法律や習慣を持つ国に対して、サービスを最適化することです。

現地化はかなり広い意味を持ち、「最適化」をどこまですべきかは客観的な答えはないと思います。

でも、世界規模のサービスを作りたい場合、以下のことを頭の片隅に入れると良いと思います。

 

法律

国境を越えると海外の法律を考慮しなければなりません。
各国の著作権法、成年の年齢、広告の掲載に関する規制などが異なるので、このような法的問題を対応できる余地をサービスを設計する段階で考えるべきだと思います。

 

日付・時間の表示

日本向けのサービスと違って、世界規模のサービスに時間を表示させる場合、世界各時間帯の対応をする必要があります。
なお、日付と時間の表し方も国によって異なります。

例えば、日本では24時間表示が人気ですが、西洋ではAM/PMで時間を表すことが一般的です。

また、2017年1月2日を英語で書くとしたらどちらが正解でしょうか?

a) 1/2/2017
b) 2/1/2017

正解は・・・どちらも。
米国であれば、b が正式ですが、英連邦の国々(イギリス・オーストラリア・南アフリカ・ニュージーランドなど)では a が正しいです。

 

色と記号

一部のサービスにしか関係ありませんが、文化によって色と記号の意味が異なります。
日本ではチームを分けたら「紅白」、正解がマル印、不正解がバツ印という文化が染み込んでいますが、海外ではチームを分けたら「赤・青」、正解がチェック、不正解がバツ印という国も存在します。

なお、「春=桜色」なども、日本独特の文化によるものです。
もちろん、現地化をする際、日本で使われる色や記号を決して使ってはいけないわけではないですが、このようなことを常に考慮しなければなりません。

Internationalization (国際化)

world_map

上記の多言語化と現地化が終われば、初めて全世界向けにサービスを公開できる状態になります。

しかし、そのままサービスを公開してしまうと多くの問題が発生します。
例えば、サービスに「人気コンテンツ」のページ等があると、多くの国のコンテンツが表示されてしまうことが考えられます。
逆に、ユーザ層がある国に偏ってしまうと、「人気コンテンツ」ページの内容がその国のコンテンツに占められてしまいます。

「Internationalization(国際化)」とは、国際規模のコミュニティを仲良く混じり合い衝突させないことです。

サービスの仕様によっては違いますが、「このサービスは海外向け」と思われないように設計することが国際化の目的です。

ガラパゴスからとびだそう

日本は多くの面白いサービスを開発する国です。
小さい企業から、大きい企業まで、様々なアイディアを活かしてサービスとして提供できることは日本の特長だと思います。

けれども、多くのサービスが日本語のみ、日本人向けにしか提供されていません。
他方、アメリカで開発されるサービスは全世界向けに提供されることが多いです。

アメリカのIT会社が世界にサービスを提供する。
日本のIT会社が日本にサービスを提供する。

個人意見に過ぎないかもしれませんが、この状況は本当にもったいないと思います。
今後、ウェブサイトやゲームやその他のアプリケーションを開発される際、是非是非、国際対応をご検討ください。

参考URL
https://en.wikipedia.org/wiki/List_of_countries_and_dependencies_by_population
https://en.wikipedia.org/wiki/List_of_countries_by_GDP_(nominal)
https://en.wikipedia.org/wiki/Date_format_by_country
 
\\インターネットに関することは何でもご相談ください//
ウェブサイトやスマートフォンアプリの国際化に関心ございませんか?
弊社までお気軽にご相談ください!
お問い合わせはこちらから。

Oculus Rift、HTC Vive、PlayStation VRを比較した感想

PSVRを体験してVRの凄さを感じその場で予約
そして自宅で個人的にViveで遊ぶ為にPCを新調しVR関連で40万程散財したカツラです。

3つとも体験した個人的な感想ですが、結論として性能としてはViveゲームとしてはPSVRです。

 

Vive
まずViveの良いところですが、他の2つにはないルームスケールという点と映像の綺麗さ専用のコントローラーがついてくるところです。

theBluを体験しましたが本当にリアルにそこにいるという感じでした。
映像の綺麗さに加え部屋の中を自由に動けるのもその一因だったと思います。
ソフトさえ充実すればVive一択で良いと思えるほどです。

 

IMG_0094
次にPSVRについての良いところは、他のVR機器に比べると安い点とゲームソフトについて期待が持てるという点です。
レーシングゲーム、キッチン、初音ミクのライブ等幾つか体験しましたが、VRとして見たところ他のVRに劣らず面白かったです。
欠点としてはロードが思った以上に長く待たされるところですね。

 

IMG_7413
最後にOculus Riftですが、残念ながらViveが届いてから使用していません。
Oculus Touchの予約が開始されましたが少し遅かったのではという感じです。

今のところのOculus Riftの印象としては、特にViveに劣っているという訳ではないですが、Oculus Touchの購入まで考えて金額、性能面を比較するとViveからルームスケールを抜いたものという認識です。

Oculus専用のタイトルで面白いものがでない限りはすすんで使用することはないと思いました。

Oculusに関してはPC不要で高品質な次世代のVRヘッドセットに期待です。

 

以上、3つのVRを実体験した個人的な比較の感想でした。

バイナリヒープの使い方

プログラマーに欠かせない、配列の処理を最適化する方法

こんにちは。開発担当のマットです。
プログラムし始めたときのことを振り返ってみました。

バイナリヒープとの出会い・・・

何か面白いアプリケーションを作ろうと思って、プログラミングを勉強している方が多くいると思います。
特にゲームを作ろうと思っている方…

私もプログラミングの勉強を始めたころ、いずれゲーム作成に挑戦しようかと思っていました。
最初に作ったプログラムはJavascriptの”Hello World”でしたが、その後とても簡単なシミュレーション・ゲームを作ることに挑戦しました。

ランダムに生成された世界地図上で、多くの国や部族が戦いあって敵を全て倒すと勝つというごく一般的なゲームでしたが、作ることはとても楽しかったです。

最初に作ってみたゲーム
最初に作ってみたゲーム・・・見るとなつかしい

世界地図のランダム生成アルゴリズムが完成し、次は敵の国を作ることに挑戦しました。
敵国の動きがあまりにも読みやすかったら困るので、AIのコーディングに結構時間をかけてしまいました。

ある程度できあがって、実行しようと思ったら、、、
ガタガタと重くなりました。
処理が重くて、敵の駒が動くのに数秒かかるようになってしまいました。

まだJavascriptの世界から卒業していなかったため、重くなっていることをブラウザのせいにし「ブラウザに頼らない言語で書いていたら問題はなかった」と信じて一旦ゲームを忘れました。

その後ある日、Javaでゲームを開発しているYoutubeシリーズを見て前に作ったゲームを思い出し、再びゲーム作成に挑戦しようと決めました〜 でも今回はJavaで!

今回は全く違うゲームですが、AIを同様に作ってみました。

今回こそ!
次はJavaで作った海賊ゲーム・・・

今回は速い!
今回は問題無い!
と思いきや、またガタガタ重くなってしまいました。。。

ブラウザのせいにしていたのは大間違いで問題は私が書いたコードにありました。

コードを必死に読んで、デバッギングをして、問題を特定できました!
一つのものすごい重いファンクションのせいでした。

そのファンクションはある配列の中の一番少ない値を返してくれるものでした。
なお、そのファンクションは一秒間に何度も呼び出される。

つまり、もし配列に1000個の値が入っているとしてその処理を一秒間に1000回読んでしまえば、毎秒毎秒100万の比較処理となってしまいます。

恐ろしいループの中のループでした。

配列の最小値、(または最大値)を取得する軽い方法はないか?と悩んで、調べたらバイナリヒープに出会いました。

バイナリヒープとは?

バイナリヒープは「二分ヒープ」とも呼ばれますが、簡単に説明するとバイナリヒープは、
必ず、最小値(または最大値)が常に最初のキーに入るよう管理をきちんとされている配列です。

つまり、配列がarrayとしたら、arrayの最小値(または最大値)は必ずarray[0]にあります。
これがあると、ループをしなくても最小値・最大値をすぐ取得することができ負荷を減少するものとなります。

でも、まさか配列をソートすることになってしまうと余計処理時間がかかりそうので具体的にどうやってするのでしょうか?

配列に潜んでいる特徴

通常、配列をこのように想像します。
0から数えるキーがありそれぞれのキーに値が当てはまります。

配列の普通の形

しかし、形を変えて想像すると配列に潜んでいた面白い特徴を見ることができます。
それぞれのキーに親子関係を作ることができるのです。

バイナリヒープとして想像したら
このように並ぶと、
array[0] の子供は array[1]とarray[2]
array[3] の子供は array[7]とarray[8]
array[5] の親は array[2]
array[4] の親は array[1]
などなど・・・

パターンは既に見られているかもしれませんが、
親から見た場合、
= n としたら
左の子供 = n*2+1
右の子供 = n*2+2

子供から見た場合、
子供 = n としたら
= (n-1)/2   *分数を捨てる

バイナリヒープでできること

このバイナリヒープに値を入れる時、
または、バイナリヒープから値を削除する時、
その値の親と子供の上下関係さえ守れば、最小値・最大値は必ず、array[0]になります。

詳しく、説明します。
*説明では、「最小値」を返すバイナリヒープを紹介しますが、最大値を返すヒープは同様な動きです。

バイナリヒープにINSERT

バイナリヒープに新しい値を挿入する時、その値を配列の一番後ろに付け加えます。
次、新しく入れた値をその親と比較します。
もし新しい値の方が大きい場合、何もしません。
けれども新しい値の方が小さい場合、親と場所を交換し動作を繰り返します。

これをきちんとすると、最小値が必ず、「ルート」(array[0])の位置に上がってきます。

先ほどの配列に「3」を挿入してみましょう。

バイナリヒープに値を挿入

バイナリヒープからPOP

最小値を配列から取って削除したい場合、動作が反対となります。
まずは最小値(array[0]にあるもの)を消し、配列の一番最後の値をarray[0]に持ってくる。
その新しくなったarray[0]を子供達と比較し、大きければ一番小さい子供と場所を交換し動作を繰り返します。

バイナリヒープのルートをPOP

バイナリヒープの特長

通常1000個の値を持っている配列の最小値を取得しようと思ったら、1000回ループしなければなりません。

でもデータをバイナリヒープの形で管理したら、最小値をループせずに取得でき最大でも9回の比較だけでヒープを整え直すことができます。
(1,000 < 2^10)

配列が大きければ大きほど最適化の効果が見られます。
例えば、1,000,000個の値を持っている配列でも最大でも、たったの19回の比較で整え直すことができます。1,000,000回ループするより速いでしょう。
(1,000,000 < 2^20)

実際のコード

私は最近Sportareの開発に関わり、Swift言語でのプログラミングが好きになりました。

そこでSwiftで書いたバイナリヒープのコードを紹介したいと思います。

通常ならばもっとコード整理しますが、以下のコードはバイナリヒープを把握するために作ったのでそのままベタ書きしました。
Swiftに慣れていない方でも、すぐわかると思います〜

これからも、頑張りましょう!

PS VRで現実逃避してみた

雨の音ってずっと聴いていられますよね。(って言ったらモテそう。。)
マエダです。

「VR!VR!VR!・・・・・」

社内ではウォーボーイズばりにみんな叫んでいる今日この頃です。
(出典:マッドマックス 怒りのデス・ロード)

で、行ってきましたソニーストア。
PlayStation®VR体験。

大阪は店舗前通路にも体験ブースを設置しおねえさんが使い方を案内してくれました。
僕を含めて体験予約者はおじさんの群れ。
客観的にみると近未来感のあるアレに見えました。(ご想像にお任せします。。)

psvr

はい、口あいてます。
あきますとも。

体験させていただいたのは深海に潜っていくコンテンツ。
「気分が悪くなったら手を挙げてお知らせくださいね」とおねえさんにご案内いただきました。

で、なんかちょっとしてから絶望的に怖いサメさんがこっちに向かってきちゃいます。

完全に気分悪くなるに決まってるじゃないですか。
コワいよ。
フツーにコワい。

でも手を挙げることなんてできません。

だっておねえさんにショボいおっさんって思われるじゃないですか。。。

そんなこんなであっという間のPS VR体験は終了しました。

「気分は大丈夫ですか?」
おねえさんは僕に優しく声をかけてくれました。

「はい、大丈夫です。楽しめました。」
平静を装った僕は汗びっしょりで説得力ゼロでした。。

Oculus Riftを体験していたからそんなに驚かないだろうと思ってましたが超コワかったし超楽しめました。(震え声)

で、VRのスゴさを知った僕はもっとVRの魅力が知りたくて以下のイベントに参加。
http://googlevrgame.peatix.com/

そこで聞いたお話では今買いのVRデバイスはRiftでもPS VRでもないとのこと。

これです。
『htc Vive』
https://www.htcvive.com/jp/

ルームスケールVRという体験。
マジパネェ。
体験したヒトにしかわからないスゴさがあるとのことでこちらでは多くは語りません。。。

VR楽しい\(^o^)/