極端なホワイトウォーターカヌー


28

かなり速いホワイトウォーター川をカヌーでaいでいます。突然、パドルが爆発し、パドルなしで川を急流で急降下する危険な状況に陥ります。幸運なことに、あなたにはまだプログラミングスキルがあるので、急流を乗り切るのを助けるために、カヌーの横にプログラムを彫ることにします。ただし、プログラムを作成するカヌーの側面には表面積があまりないため、プログラムをできるだけ短くする必要があります。

川は8 x 16グリッドとして表すことができます。列にの番号0を付け7、行にの番号0を付け15ます。

        y
--------15
--------14
--------13
--------12
--------11
--------10
--------9
--------8
--------7
--------6
--------5
--------4
--------3
--------2
--------1
--------0
01234567
x

上:障害物のない完全に穏やかな普通の川。当然、これはあなたがいる川ではありません。

座標(4、0)から開始し、そこから(0,1)岩(oこれらの例ではで表される)に当たるまで、制御不能に川(ベクター)を上昇します。岩にぶつかると、55%の確率で岩を左に移動し(ベクトル(-1,1))、45%の確率で岩を右に移動します(つまりベクトル(1,1))。カヌーが左端または右端の列にある場合、常に中央に向かって移動します。岩がない場合は、まっすぐ上に移動します。

        y
----x---15
----xo--14
-o--x---13
----x---12
---ox---11
---x----10
---xo---9
---ox---8
----xo--7
-----x--6
----ox--5
-o--x---4
----x---3
----xo--2
----x---1
----x---0
01234567

上:キャラクターを使用して表された、カヌーが通る可能性のあるルート x

川の地図を指定して、特定の列でカヌーが終了する確率を出力するプログラムを作成します。

プログラムに都合のよい方法(STDIN、コマンドライン引数、raw_input()ファイルからの読み取りなど)で入力を受け入れます。入力の最初の部分は、プログラムが確率を見つける列を表す0〜7の単一の整数です。その後x,yに、石の位置を表す形式のタプルのリストがあります。

例:

入力:

4 4,1 5,5 3,5

これは、位置(4,1)、(5,5)、および(3,5)に岩がある川を示し、4列目でカヌーが終了する確率を求めます。

出力:

0.495

この例では、岩の位置が対称であり、問​​題を二項分布で解決できることに注意してください。これは常に当てはまるわけではありません!

また、川は常に交差可能です。つまり、水平方向に互いに隣接する2つの岩はありません。不可能なケースの例については、グレンのコメントを参照してください。

これはコードゴルフなので、キャラクターの数が最も少なくなります。仕様が明確でない場合は、コメントで質問してください。


8
皮肉なことに、このプログラムは実際には誰も急流を乗り切るのに役立ちません。それは、彼らはしかし生き残るだろうがどのように可能性を伝えない。
アブサン

1
2つ以上の岩が並んでいるとどうなりますか?たとえば、マップが「0 1,0 1,1」の場合、カヌーは1,1で岩に衝突します。コースを完了する()状態が禁止され、または(b)の確率は0である
グレンランダース-Pehrson

1
ああ、わかった。すみません、私はその部分を逃しました。
ドアノブ

3
最終的な考え:「おそらく、プログラム可能なカヌーを構築することは、爆発性パドルを使用する問題に対する最善の解決策ではなかったでしょう。」
カイ

2
パドルが爆発したときの様子を見てみたい。
ロビーWxyz 14

回答:


4

GolfScript、105文字

~](\2/:A;8,{4=}%15,{:B;{20*}%A{~B={[\\,.1,*\[2$=..20/9*:C-\~)C]+(1,\+1,6*2$8>+]zip{{+}*}%.}*;}/}/=20-15?*

意図したよりもはるかに長くなったGolfScriptバージョン-しかし、異なるアプローチでの各試行はさらに長くなりました。入力はSTDINで指定する必要があります。

例:

> 4 4,1 5,5 3,5
99/200

注釈付きコード:

# Evaluate the input
#  - stack contains the first number (i.e. column)
#  - variable A contains the rock coordinates (pairs of X y)
#    where X is an array of length x (the coordinate itself)
~](\2/:A;

# Initial probabilities
#  - create array [0 0 0 0 1 0 0 0] of initial probabilities
8,{4=}%

# Loop over rows 
15,{:B;           # for B = 0..14
  {20*}%          #   multiply each probability by 20
  A{              #   for each rock 
    ~B={          #     if rock is in current row then
                  #       (prepare an array of vectors [v0 vD vL vR] 
                  #       where v0 is the current prob. before rocks,
                  #       vD is the change due to rocks,
                  #       vL is a correction term for shifting out to the left
                  #       and vR the same for the right side)
      [\\         #       move v0 inside the array
      ,           #       get x coordinate of the rock
      .1,*        #       get [0 0 ... 0] with x terms
      \[2$=       #       get x-th item of v0
      ..20/9*:C-  #       build array [0.55P -P 0.45P]
      \~)C]+      #       and append to [0 0 ... 0]
      (1,\+       #       drop the leftmost item of vD and prepend [0] again
                  #       which gives vL
      1,6*2$8>+   #       calculate vR using the 8th item of vD
      ]           #       
      zip{{+}*}%  #       sum the columns of this list of vectors
      .           #       dummy dup for end-if ;
    }*;           #     end if
  }/              #   end for
}/                # end for

# take the n-th column and scale with 20^-15
=
20-15?*

11

ルビー、204の 191 172文字

c,*r=gets.split
o=[0]*8
s=->x,y,p{y>14?o[x]+=p :(r.index("#{x},#{y+=1}")?(x<1?s[x+1,y,p]:(x>6?s[x-1,y,p]:(s[x-1,y,p*0.55]+s[x+1,y,p*0.45]))):s[x,y,p])}
s[4,0,1]
p o[c.to_i]

個々の結果の確率を追跡しながら、可能性のあるすべての結果を再帰的にシミュレートし、の場合、その確率を累積カウンターに追加しy == 15ます。

派手なトリック:

  • c,*r=gets.split-「splat」演算子(*)は、残りのすべての要素をgets.split取得し、r配列に貼り付けます

  • next {something} if {condition}:基本的にと同等

    if {condition}
        {something}
        return
    end
    

    以下からの進化によって「発見」if condition; something; return; endreturn something if conditionするbreak something if condition、そして私は、私はそれが(もちろん、それがなかったもの)うまくいくかどうかを確認するために短い「ループ演算子」をしようと考え出しました。

  • @MartinBüttnerに、チェーンの三項演算子(最終的には上記のゴルフコードの3行目となる)の使用を提案し、上記のポイント(19(!)文字を節約)を削除してくれたことに感謝します。

    しかし、私はそれらでやや派手なトリックを使用しました:s[foo],s[bar]1つのステートメントでの2つのメソッド呼び出しに対してRubyでは機能しないことに気付きました。そのため、最初は(_=s[foo],s[bar])(ダミー変数)に変更しましたが、戻り値を追加して捨てることができることに気付きましたs[foo]+s[bar]。呼び出しがするので、これはのみ動作sしかへの他の呼び出し「戻る」でしょうsか数を(o[x]+=p私がチェックを心配する必要はありませんので、) nil

  • その他のさまざまな最適化:p代わりにputs、番号を印刷する<1代わりに==0他の場所、および同様の比較(カヌーが川を離れることはありませんので)[0]*8必ず「値渡し」されているRubyの番号などの初期確率について

ゴルフをしていない:

column, *rocks = gets.chomp.split
outcomes = Array.new(8, 0)
simulate = -> x, y, probability {
    if y == 15
        outcomes[x] += probability
    elsif rocks.index("#{x},#{y + 1}")
        case x
        when 0 then simulate[x + 1, y + 1, probability]
        when 7 then simulate[x - 1, y + 1, probability]
        else
            simulate[x - 1, y + 1, probability * 0.55]
            simulate[x + 1, y + 1, probability * 0.45]
        end
    else
        simulate[x, y + 1, probability]
    end
}
simulate[4, 0, 1.0]
p outcomes
puts outcomes[column.to_i]

これらすべてnext X if Yをネストされた3項演算子にまとめるのはまだ短いのではないでしょうか?ただし、Rubyのヒントに追加することをお勧めします。
マーティンエンダー

@MartinBüttnerうん、それは実際になんと19文字短い!ありがたいことに、それは
途方もなく

5

C#418 364バイト

STDINからの入力を予期する完全なC#プログラム。川のすべての場所の配列に岩を読み込んで効果的に地図を作成し、結果を出力する前に8桁の10進数配列の周りで16回の移動確率の反復を実行します。

using C=System.Console;class P{static void Main(){var D=C.ReadLine().Split();int i=0,j=D.Length;var R=new int[8,16];var p=new decimal[8];for(p[4]=1;--j>0;)R[D[j][0]-48,int.Parse(D[j].Substring(2))]=1;for(;i<16;i++){var n=new decimal[j=8];for(;j-->0;)if(R[j,i]>0){n[j<1?1:j-1]+=p[j]*0.55M;n[j>6?6:j+1]+=p[j]*0.45M;}else n[j]+=p[j];p=n;}C.WriteLine(p[D[0][0]-48]);}}

フォーマットされたコード:

using C=System.Console;

class P
{
    static void Main()
    {
        var D=C.ReadLine().Split();
        int i=0,j=D.Length;
        var R=new int[8,16];
        var p=new decimal[8];

        for(p[4]=1;--j>0;) // read rocks into map (R)
            R[D[j][0]-48,int.Parse(D[j].Substring(2))]=1;

        for(;i<16;i++) // move up the river
        {
            var n=new decimal[j=8];
            for(;j-->0;)
                if(R[j,i]>0)
                { // we hit a rock!
                    n[j<1?1:j-1]+=p[j]*0.55M;
                    n[j>6?6:j+1]+=p[j]*0.45M;
                }
                else
                    n[j]+=p[j];
            p=n; // replace probability array
        }

        C.WriteLine(p[D[0][0]-48]); // output result
    }
}

「goes to」演算子(for(;j-->0;))を使用する場合は+1 。あなたはしかし、最後に置き換えることにより、文字のカップルを取り除くことができますC.WriteLineによってC.Write。また、float代わりに使用するdecimal場合は、さらに数バイト節約できます。
クリストフベームヴァルダー14

@HackerCowの標準プラクティス;)forループを最大限に活用するようになりました!正確ではないdecimalので、私は使用してfloatいますが、これらの問題は10進数で解決するはずですが、あなたが言うようにそれでうまくいくかもしれません。4バイトでこのサイズのプログラムを編集する必要はないと思うC.Writeよりも、おそらく仕様に近いため、これ以上ゴルフをすることができたら、私は入れますC.WriteLine;)
VisualMelon 14

2

Haskell、256バイト

import Data.List
m=map;v=reverse
a p n x=take n x++(x!!n+p:drop(n+1)x)
l=abs.pred
o[_,n]s=n#(s!!n)$s
n#p=a(11*p/20)(l n).a(9*p/20)(7-(l$7-n)).a(-p)n
b=0:0:0:0:1:b
k(c:w)=(foldl1(.)$m o$v$sort$m(v.read.('[':).(++"]"))w)b!!read c
main=getLine>>=print.k.words

これは、使用されていないいくつかのトリックとともに、非常に未使用のバージョンです。

import Data.List

-- Types to represent the distribution for the canoe's location
type Prob = Double
type Distribution = [Prob]

-- Just for clarity..
type Index = Int

-- An Action describes some change to the probability distribution
-- which represents the canoe's location.
type Action = Distribution -> Distribution

-- Helper to add k to the nth element of x, since we don't have mutable lists.
add :: Index -> Prob -> Action
add n k x = take n x ++ [p] ++ drop (n + 1) x
    where p = k + x!!n  

-- A trick for going finding the index to the left of n,
-- taking the boundary condition into account.
leftFrom n = abs (n - 1)

-- A trick for getting the other boundary condition cheaply.
rightFrom = mirror . leftFrom . mirror
    where mirror = (7 -)

-- Make the action corresponding to a rock at index n.
doRock :: Index -> Action
doRock n p = (goLeft . goRight . dontGoForward) p
    where goLeft  =  (leftFrom n) `add` (p_n * 11/20)
          goRight = (rightFrom n) `add` (p_n * 9/20)
          dontGoForward =  (at n) `add` (-p_n)
          p_n = p!!n
          at = id

initialProb = [0,0,0,0,1,0,0,0]

-- Parse a pair "3,2" ==> (3,2)
readPair :: String -> (Index,Index)
readPair xy = read $ "(" ++ xy ++ ")"

-- Coordinate swap for the sorting trick described below.
swap (x,y) = (y,x)

-- Put it all together and let it rip!
main = do
    input <- getLine
    let (idx : pairs) = words input
    let coords = reverse . sort $ map (swap . readPair) pairs
    let rockActions = map (doRock . snd) coords
    let finalProb = (foldl1 (.) rockActions) initialProb
    print $ (finalProb !! read idx)

私が最後に使用したトリックは、1つの列の岩が実際にわずかな量だけ分離されているかのように振る舞うことができることに注意することでした。つまり、すべての岩を同時に適用するのではなく、同じ列の各岩に確率分布トランスフォーマーを任意の順序で順番に適用できます。これは、問題が水平方向に隣接する2つの岩を許可しないためにのみ機能します。

そのため、プログラムは各岩の位置を、岩のy座標で順序付けられた確率分布変換器に変換します。トランスフォーマーは順番にチェーンされ、初期確率分布に適用されます。それだけです!


2

Perl 169バイト

STDINから読み取ります。

$_=<>;s/ (.),(\d+)/$s{$1,$2}=1/eg;/./;$x{4}=1.0;for$y(1..15){for$x(0..7){if($s{$x,$y}){$x{$x-1}+=$x{$x}*($x%7?.55:1);$x{$x+1}+=$x{$x}*($x%7?.45:1);$x{$x}=0}}}print$x{$&}

かなり単純で、暗黙的に列-1と8を使用して境界のケースを滑らかにします。隣接する石が存在しないため、確率を次の各レベルに安全に伝播できます。したがって、1回の実行で十分です。


2

PHP、358

可能性のあるパスとその確率を判断するために脳力を使用することは難しく、おそらく1,000,000カヌーの事故をシミュレートするよりも多くのコードが必要になります。ああ、人類!

define('MX',7);
define('MY',16);
define('IT',1000000);
error_reporting(0);

function roll(){return rand()%100 > 44;}

function drift($rocks,$print=false) {
    for($px=4,$py=0;$py<MY;$py++) {
        if(isset($rocks[$px][$py])){
            if(roll()) $px--;
            else $px++;
        }
        else if($px==0) $px++;
        else if($px==MX) $px--;
        if($print) {
            for($i=0;$i<MX;$i++){
                if($i==$px) echo 'x';
                else if(isset($rocks[$i][$py])) echo 'o';
                else echo '-';
            }
            echo " $py\n";
        }
    }
    return $px;
}

$input = $argv[1];
$tmp = explode(' ',$input);
$end_target = array_shift($tmp);
$rocks = array();
array_map(function($a) use(&$rocks) {
    list($x,$y) = explode(',',$a);
    $rocks[$x][$y]=1;
}, $tmp);

$results=array();
for($i=0;$i<IT;$i++) {
    $results[drift($rocks)]++;
}

drift($rocks, true); // print an example run

foreach($results as $id=>$result) {
    printf("%d %0.2f\n", $id, $result/IT*100);
}

例:

php river.php "4 4,1 5,5 3,3 6,2 9,4 12,3 13,5"
----x-- 0
---xo-- 1
---x--o 2
--xo--- 3
--x---- 4
--x--o- 5
--x---- 6
--x---- 7
--x---- 8
--x---- 9
--x---- 10
--x---- 11
--x---- 12
--x---- 13
--x---- 14
--x---- 15
4 49.53
2 30.18
6 20.29

ゴルフ:

<? function d($r){for($x=4,$y=0;$y<16;$y++){if(isset($r[$x][$y])){if(rand()%100>44)$x--;else $x++;}elseif($x==0)$x++;elseif($x==7)$x--;}return $x;}$t=explode(' ',$argv[1]);$e=array_shift($t);$r=array();array_map(function($a)use(&$r){list($x,$y)=explode(',',$a);$r[$x][$y]=1;},$t);$c=0;for($i=0;$i<1000000;$i++){if(d($r)==$e)$c++;}printf("%.4f", $c/1000000);

このバージョンは、きれいな印刷を行わず、指定された位置にカヌーが着陸するフロート確率を出力します。

# php river_golf.php "4 4,1 5,5 3,3 6,2 9,4 12,3 13,5"
0.4952

私はここで入力フォーマットが少しオフになっていると思いますが、例えばriver.php「5 4,4 1,5 5,3 3,6 2,9 4,12 3,13」に0.561375を与えるべきである
マット・ヌーナン

@MattNoonan昨日は荒れた日だった。私は...それを修正することができるはず
Sammitch

2

PHP、274

GolfScriptを読み書きして自分の命を救うことはできませんが、@ Howardの提出を一glすることで、100万人のカヌーの事故をシミュレートするよりも良い方向に導かれました。

開始位置の確率の配列から始めて、岩に遭遇するたびにそれらの数を単純に分割できます。

function psplit($i){ return array(.55*$i,.45*$i); }
function pt($a) {
    foreach($a as $p) {
        printf("%1.4f ", $p);
    }
    echo "\n";
}

$input = $argv[1];
$tmp = explode(' ',$input);
$end_target = array_shift($tmp);
$rocks = array();
array_map(function($a) use(&$rocks) {
    list($x,$y) = explode(',',$a);
    $rocks[$x][$y]=1;
}, $tmp);

$state = array(0,0,0,0,1,0,0,0);
pt($state);
for($y=1;$y<16;$y++){
    for($x=0;$x<8;$x++){
        if(isset($rocks[$x][$y])){
            echo('   o   ');
            list($l,$r)=psplit($state[$x]);
            $state[$x]=0;
            $state[$x-1]+=$l;
            $state[$x+1]+=$r;
        } else { echo '   -   '; }
    }
    echo "\n";
    pt($state);
}

出力例:

# php river2.php "4 4,1 5,5 3,3 6,2 9,4 12,3 13,5"
0.0000 0.0000 0.0000 0.0000 1.0000 0.0000 0.0000 0.0000
   -      -      -      -      o      -      -      -
0.0000 0.0000 0.0000 0.5500 0.0000 0.4500 0.0000 0.0000
   -      -      -      -      -      -      o      -
0.0000 0.0000 0.0000 0.5500 0.0000 0.4500 0.0000 0.0000
   -      -      -      o      -      -      -      -
0.0000 0.0000 0.3025 0.0000 0.2475 0.4500 0.0000 0.0000
   -      -      -      -      -      -      -      -
0.0000 0.0000 0.3025 0.0000 0.2475 0.4500 0.0000 0.0000
   -      -      -      -      -      o      -      -
0.0000 0.0000 0.3025 0.0000 0.4950 0.0000 0.2025 0.0000
   -      -      -      -      -      -      -      -
0.0000 0.0000 0.3025 0.0000 0.4950 0.0000 0.2025 0.0000
   -      -      -      -      -      -      -      -
0.0000 0.0000 0.3025 0.0000 0.4950 0.0000 0.2025 0.0000
   -      -      -      -      -      -      -      -
0.0000 0.0000 0.3025 0.0000 0.4950 0.0000 0.2025 0.0000
   -      -      -      -      -      -      -      -
0.0000 0.0000 0.3025 0.0000 0.4950 0.0000 0.2025 0.0000
   -      -      -      -      -      -      -      -
0.0000 0.0000 0.3025 0.0000 0.4950 0.0000 0.2025 0.0000
   -      -      -      -      -      -      -      -
0.0000 0.0000 0.3025 0.0000 0.4950 0.0000 0.2025 0.0000
   -      -      -      -      -      -      -      -
0.0000 0.0000 0.3025 0.0000 0.4950 0.0000 0.2025 0.0000
   -      -      -      -      -      -      -      -
0.0000 0.0000 0.3025 0.0000 0.4950 0.0000 0.2025 0.0000
   -      -      -      -      -      -      -      -
0.0000 0.0000 0.3025 0.0000 0.4950 0.0000 0.2025 0.0000
   -      -      -      -      -      -      -      -
0.0000 0.0000 0.3025 0.0000 0.4950 0.0000 0.2025 0.0000

ゴルフ:

<? $t=explode(' ',$argv[1]);$e=array_shift($t);$r=array();foreach($t as $n){list($j,$k)=explode(',',$n);$r[$j][$k]=1;}$s=array(0,0,0,0,1,0,0,0);for($y=1;$y<16;$y++){for($x=0;$x<8;$x++){if(isset($r[$x][$y])){$s[$x-1]+=$s[$x]*.55;$s[$x+1]+=$s[$x]*.45;$s[$x]=0;}}}echo $s[$e];

実行例:

# php river2_golf.php "4 4,1 5,5 3,3 6,2 9,4 12,3 13,5"
0.495

1

ハスケル、237

カヌーにghcがインストールされていることを願っています...

無限のリストを持つトリックは、Matt Noonanから盗まれました。

import Data.List
r=reverse
(a:b:x)%0=0:a+b:x
x%7=r(r x%0)
x%n=take(n-1)x++(x!!(n-1)+x!!n*0.55:0:x!!(n+1)+x!!n*0.45:drop(n+2)x)
q=0:0:0:0:1:q
u(w:x)=(foldl(%)q.map last.sort.map(r.read.('[':).(++"]"))$x)!!read w
main=interact$show.u.words

ロジックが正しくなったことを願っていますが、Mattの例の"5 4,4 1,5 5,3 3,6 2,9 4,12 3,13"yields 0.5613750000000001とOPの例の"4 4,1 5,5 3,5"yields 0.49500000000000005は、いくつかの浮動小数点エラーを除けば正しいようです。

ここに実際にあります:

>>> echo 5 4,4 1,5 5,3 3,6 2,9 4,12 3,13 | codegolf
0.5613750000000001
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.