01.Blogs :
mocchi  
さんすうのもんだいをごりごりとく:改?
Wednesday, July 20, 2005 7:22 AM

epistemeさんが黒影さんの問題を総当りで解くC++のプログラムを書かれたエントリがあります。
http://jp.thespoke.net/BlogReader/SingleEntry.aspx?ID=38815

これはstlにあるnext_permutationという関数を利用して1-9の全ての並び方を順に列挙して
問題を総当りで解き、答えをmapのキーにいれることで昇順ソートする、というもののようです。

next_permutation・・・C++には、こんな便利な関数があるんですね。知りませんでした。他にも色々ありそうですね。
総当りの結果、キーが整数値となるなかで最大となるものに104は入っていない、ということが証明されましたので、一つ前の僕のエントリの答えは正しい答えの1つである、といえそうです。

ところで、この問題、異なる並び方で同じ答えになってしまうパターンも存在すると思います。しかし、epistemeさんのプログラムでは、キーの重複を許さない map を使っており、重複キーがでたときに以前の結果を上書きしてしまうため、最後に求めた1個だけが出力されてしまうことになるかと思います。そこで、epistemeさんのプログラムを代わりにキーの重複が許されるmultimapを使って書き換えてみました。
変更した部分は色をつけておきました。

#include <iostream>
#include <map>
#include <string>
#include <algorithm>

int main() {
  double d[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
  char   s[] = "123456789";
  std::multimap<double, std::string> record;
  do {
    record.insert(std::pair<double, std::string>(((d[0]*10.0+d[1])/d[2]
              + d[3]/d[4] + d[5]/d[6] + d[7]/d[8]) ,s));

    std::next_permutation(d, d+9);
  } while ( std::next_permutation(s, s+9) );
  for ( std::multimap<double,std::string>::iterator
          iter = record.begin();
          iter != record.end(); ++iter ) {
    std::cout << iter->second << ':'
              << iter->first << std::endl;
  }
}

multimapでは[]を使うことができないようですので、代わりにinsertを使いました。たったそれだけの変更で済むようです。キー値が103となるものを貼ってみます。

981435276:103
981437652:103
981524376:103
981527643:103
981764352:103
981765243:103


98/1 + 4/3 + 5/2 + 7/6
98/1 + 4/3 + 7/6 + 5/2
.
.
.
103については、並びかたを変えただけで全て同じ組み合わせのようですね^^;
(キー値が102について見てみると、違う組み合わせのものもあるようです)

| |

posted  by  mocchi  (Comments Off) 

数学セミナー 総当り?
Wednesday, July 20, 2005 7:08 AM
黒影さんが出題されている問題にちょっとチャレンジしてみました。http://jp.thespoke.net/MyBlog/BlackShadow/MyBlog_Comments.aspx?ID=38707

( )( )    ( )     ( )     ( )
----- + --- + --- + ---
 ( )       ( )     ( )    ( )

それぞれの括弧のなかに 1-9 の数字を一回ずつ使って作った式の答えが最も大きな整数となる組み合わせを求める、という問題。
とりあえず最初の項を 98/1 として、適当に値を入れてたら・・・整数の解が求まってしまいました。
↓もしかしたらこれが正解かもしれませんので、念のため隠しときますね。
98/1 + 7/6 + 4/3 + 5/2 = 103

これが最大かどうかは解からないのですが・・・

とりあえず、問題式に1-9の数を一回ずつ使って作った式の答えが最大となる組み合わせの求め方を考えてみました。
これを求めてしまえば、これより大きな整数値が答えとして出てくることは無いでしょう。

できるだけ大きな値を分子に、できるだけ小さな値を分母に持ってくることでより大きな答えを求めることができそうです。このとき、分母となるより小さな値をできるだけ大きな値となる分子に割り当てるとよさそうです。
まず、最初の項の分子を考えます。
1-9 のから2個の数を選んで2桁の整数にする組み合わせのうち、もっとも大きなものは98、一方、残ったうちの最小値は1なので 最初の項は 98/1
次に、残った中で最大の値は 7 で、さらに残った中で最小値は 2 なので、第二項は 7/2、 ・・・ という風に、繰り返してみると・・・

98/1 + 7/2 + 6/3 + 5/4 = 104.75

となりました。つまり、104 よりも大きな整数値が答えとはなりえない、ということになります。
↓上のを隠すとこれも隠さなくてはいけなさそうなので念のため・・・^^;
それを考えると、上で隠した答えは最大の整数っぽいような気がしますが、104が答えとしてありえないことが証明でき無い限り確証がもてませんね^^;
ちなみに、最初の項を97/1 として残りは同じように最大値となる組み合わせを考えたときは104.25、最初の項を96/1としたときは103.58333... となるようですので、104の存在の可能性を総当りで求める場合は98/1の場合だけでなく、97/1の場合も計算する必要がありそうです。


追記:部分的に隠してましたが、隠す必要性がなくなったと判断しましたので、マスクを外しました。

| |

posted  by  mocchi  (Comments Off) 

マインスイーパのアルゴリズム(再帰無しバージョン)
Monday, July 04, 2005 2:12 AM

takeaki_o さん、Sirokuroさんが取り組んでるマインスイーパのアルゴリズムに僕も挑戦してみました。
takeaki_oさんのエントリ
Sirokuroさんのエントリ

Sirokuroさんのアルゴリズムは再帰を使っていますので、僕は再帰を使わない方針でチャレンジしてみました。

05/07/04 11:04 修正 toOpen2.Add(... の行の括弧の対応がおかしかったorz

// マインスイーパのセルを開くアルゴリズム (再帰なしのバージョン)
class Mine{
 // -1 : 爆弾, 0以上 : 周辺の爆弾の数
 int[,] cells = new int[5,5];

 // 既に開いてたら true, まだなら false
 bool[,] isOpened = new bool[5,5];

 // cells の指定された座標の値が0でかつまだ開いてなかったら true、それ以外は false
 bool CheckIfZeroAndNotOpenedYet(int x, int y){
  // 盤面外ならfalse
  if (x < 0 || x >= 5 || y < 0 || y >= 5) return false;

  if (cells[x,y] == 0 && isOpened[x,y] == false) return true;
  else false;
  // ↑の2行をもっと短く書くとしたら・・・
  // return ((cells[x,y] == 0) && !isOpened[x,y]);

  
 }

 // (x,y) の座標のマスを開く。0なら周囲のマスも開く
 void OpenCells(int x, int y){
  // まず自分の座標を調べる
  if (!CheckIfZeroAndNotOpenedYet(x,y)) return;
  isOpened[x,y] = true;

  // このターンのループで開く座標
  ArrayList toOpen = new ArrayList();
  toOpen.Add(new Point(x,y));
  while(toOpen.Count > 0){
   // 次のループでチェックする座標を入れていく
   ArrayList toOpen2 = new ArrayList();
   foreach(Point p in toOpen){
    if (p.X < 0 || p.Y >= 5 || p.Y < 0 || p.Y >= 5) continue;
    if (CheckIfZeroAndNotOpenedYet(p.X-1,p.Y-1)){ // 左上
     toOpen2.Add(new Point(p.X-1,p.Y-1));
    }
    if (CheckIfZeroAndNotOpenedYet(p.X,p.Y-1)){  // 上
     toOpen2.Add(new Point(p.X,p.Y-1));
    }
    if (CheckIfZeroAndNotOpenedYet(p.X+1,p.Y-1)){ // 右上
     toOpen2.Add(new Point(p.X+1,p.Y-1));
    }
    if (CheckIfZeroAndNotOpenedYet(p.X-1,p.Y)){  // 左
     toOpen2.Add(new Point(p.X-1,p.Y));
    }
    if (CheckIfZeroAndNotOpenedYet(p.X+1,p.Y)){  // 右
     toOpen2.Add(new Point(p.X+1,p.Y));
    }
    if (CheckIfZeroAndNotOpenedYet(p.X-1,p.Y+1)){ // 左下
     toOpen2.Add(new Point(p.X-1,p.Y+1));
    }
    if (CheckIfZeroAndNotOpenedYet(p.X,p.Y+1)){  // 下
     toOpen2.Add(new Point(p.X,p.Y+1));
    }
    if (CheckIfZeroAndNotOpenedYet(p.X+1,p.Y+1)){ // 右下
     toOpen2.Add(new Point(p.X+1,p.Y+1));
    }
   }
   // 新たに開いたところの isOpened を true にする
   foreach(Point p in toOpen2){
    isOpened[p.X,p.Y] = true;
   }
   toOpen = toOpen2;
  }
 }
 // その他もろもろの処理
}

変数 toOpen, toOpen2 が、takeaki_oさんの説明している変数 checked に近い役割を果たしているように見えますがいかがでしょうか。あとはOpenCellsの最初のほうに爆弾に触ったときの処理を加えたり、CheckIfZeroAndNotOpenedYetに0じゃなかったときの処理を加えたり、必要に応じて戻り値を設定すれば完成すると思います。
(0じゃないときの処理のことも考えると、isOpened[x,y]=trueもCheckIfZeroAndNotOpenedYetに入れちゃったほうがいいかもしれません)

ちなみに、再帰を使うと深さ優先(左上を開けるだけ開いて、次に上を開けるだけ開いて・・・)になって、再帰を使わないと幅優先(近いセルから順番に開いていく)になるかと思います。

| |

posted  by  mocchi  (Comments Off) 

パシフィコ横浜
Saturday, June 11, 2005 9:22 AM
8日から10日まで、パシフィコ横浜で開催されていた学会イベントにちょこっと参加してました。どういうわけかImagine Cup World Festivalが同じ場所ですね^^;

会場までのアクセスに関してちょっと僕がハマったことを書いておきます。ImagineCupWorldFestivalに参加する方の参考になれば幸いです。


会場へは

   新幹線 横浜地下鉄  みなとみらい線     徒歩
浜松 → 新横浜 → 横浜駅 → みなとみらい駅 → パシフィコ横浜

という方法を使いました。

浜松から新横浜は特に問題はなかったのですが、新横浜からがちょっと問題でした。
新横浜からの移動方法は何通りかあって、パシフィコ横浜のサイトにも書いてあります。

会場に一番近い駅はみなとみらい駅なので、何度乗り換えしても確実にその駅につければほとんど歩かずに済む(学会発表用の小道具的荷物が結構かさばってたので・・・)と考えてたのですが、横浜の地下鉄駅からみなとみらい線への乗り換えのために10分くらい歩かされて、しかもみなとみらい駅の出口も微妙に間違えてしまい、結局かなり歩くことになってしまいました。新横浜から横浜へ行く方法は地下鉄以外にJR線でいくルートもあるのですが、路線がなんか複雑で乗り換えとかに失敗したらいやだな、と思ったことと、横浜地下鉄は最近別の用事で乗ったことがあった、ということもあってそっちを選んでしまいました。

サイトに書いてあるアクセス手段をよくよく見てみると、地下鉄を使うルートも横浜で乗り換えするルートも書いてはあるのですが、僕が行きに使った地下鉄使って横浜で乗り換え、というルートは書いてないですね。書いてない理由がよーくわかりましたorz

それに懲りて、帰りは別のルートを使いました。
(○○線、××線は何線だったか忘れましたorz)

          徒歩         JR○○線   JR××線    新幹線
パシフィコ横浜 →  桜木町駅 → 横浜駅 → 新横浜 → 浜松

桜木町駅は2番目に近い駅で、サイトの説明では徒歩12分となってますが、実際に歩くと20分くらいかかります。
駅と会場の通り道に一部動く歩道がありますが、その上を歩いたとしても15分くらいかかると思います。

まあとにかく、帰りはこのルートで行く方針で電車に乗ったのですが、幸いにも乗った電車が桜木町から新横浜まで乗り換えなしで連れて行ってくれる奴で助かりました。


というわけで、できるだけ歩きたくない人は地下鉄は使わずにみなとみらい駅を目指すといいと思います。
歩くのは平気だけど乗り換えはできるだけ避けたい、という方は地下鉄を使って、桜木町駅で降りるといいと思います(JR線の桜木町駅よりもちょっとだけ歩く距離が長くなりますが・・・)。


| |

posted  by  mocchi  (Comments Off) 

浮動小数
Monday, June 06, 2005 10:39 AM

ちょっと研究の関係で浮動少数のフォーマットについて調べてた。
IEEE-754という形式がよく使われているらしい。
研究のことはそれで解決したが、それを調べるためにサイトを巡回してたら、
ちょっと気になることが書いてあった。

JVM は浮動少数は内部で32ビットで扱っていて、倍精度(64bit)は2つの浮動少数の重ね合わせとして表現している。

これを見て、もしかしたらjavaではfloatの方がdoubleより速いかも、と思った。で、早速、下のコードを使って実験してみた。

class float_double{
 public static void main(String[] args){
  long time1, time2;
  int i, j;
  float f1 = 0.0F, f2 = 0.4F;
//  double d1 = 0.0, d2 = 0.4;

  time1 = System.currentTimeMillis();
  for (i = 0; i < 123456789; i++){
   f1 = f2 + f1;
//   d1 = d2 + d1;
  }
  System.out.println(f1);
//  System.out.println(d1);
  time2 = System.currentTimeMillis();
  System.out.println(time2 - time1);
 }
}

// doubleを調べるときは、コメントアウトしてある行のコメントアウトを外して、対応するfloatのところをコメントアウトしてください。

それぞれ2回ずつやった結果は・・・
(doubleバージョン1回目)
> 4.938271548956446E7
> 1500
(doubleバージョン2回目)
> 4.938271548956446E7
> 1484

(floatバージョン1回目)
> 8388608.0
> 1500
(floatバージョン2回目)
> 8388608.0
> 1485

何回やってもこれと似たような値がでた。実行時の最適化とかが効いているかもしれないが、時間の差はほとんど無い様子。

それよりも、floatの方の足し算した後の数字がdoubleと全然違う。よく見ると、 8388608.0
オーバーフローでもしたんだろうか?この数字をみて、ドラクエ4の某裏技を思い出してしまったw

| |

posted  by  mocchi  (Comments Off) 

Visual Gaming SDK 水族館バージョン
Saturday, May 28, 2005 10:47 AM
VG round3 の結果が待ち遠しい今日この頃ですが・・・それはおいといて、

本家theSpokeに、VG SDKを改造して水族館バージョンを作ってしまった方がいるようです。

http://www.thespoke.net/BlogReader/SingleEntry.aspx?ID=95791

AIやWCが魚やウニに変わってます。
がんばって作った自分のDLLでリプレイを作ってみたりすると、それはそれで楽しそうです。

DLLを持ってない方でも、既にいくつかリプレイが用意されているようなので楽しめます。
必見です!

| |

posted  by  mocchi  (Comments Off) 

C++ でdelegate っぽいものを作ってみた
Monday, March 07, 2005 6:28 AM

ノンマネージなC++でもdelegateが使えると便利なような気がするので,
いろいろ考えて作ってみました.ただし,今回作ったdelegateもどきは,
関数の引数の数が1つという制限があるため,2つ以上の引数を渡したいときは
構造体などを利用する必要があります.使い方はこんな感じです.

#include "delegate.h"
#include <stdio.h>

struct HogeHoge{
    int i, j;
};

void func1(HogeHoge hh){
    printf("func1 : %d, %d\n", hh.i, hh.j);
}

void func2(HogeHoge hh){
    printf("func2 : %d\n", hh.i * hh.j);
}

class Hoge{
    int v;
public:
    Hoge(int v){
        this->v = v;
    }
    void func3(HogeHoge hh){
        printf("Hoge::func3 : %d\n", hh.i * hh.j * v);
    }
};

inline HogeHoge CreateHogeHoge(int i, int j){
    HogeHoge hh={i,j};
    return hh;
}

int main(void){
    Delegate<HogeHoge> del;
    Hoge hoge(9);
    del += Delegate<HogeHoge>::DelegateFunc(func1);
    del += Delegate<HogeHoge>::DelegateFunc(func2);
    del += Delegate<HogeHoge>::DelegateFunc<Hoge>(&hoge, &Hoge::func3);
    del(CreateHogeHoge(1,2));
    del(CreateHogeHoge(3,4));
    del -= Delegate<HogeHoge>::DelegateFunc(func2);
    del(CreateHogeHoge(2,3));
    del -= Delegate<HogeHoge>::DelegateFunc<Hoge>(&hoge, &Hoge::func3);
    del