Berkano Berarung Ekstrim

28

Anda mengayuh sampan menyusuri sungai arung yang cukup cepat. Tiba-tiba, dayung Anda meledak, dan Anda mendapati diri Anda dalam situasi berbahaya yang melaju cepat di sungai tanpa dayung. Untungnya, Anda masih memiliki keterampilan pemrograman, sehingga Anda memutuskan untuk mengukir program di samping sampan untuk membantu Anda selamat dari jeram. Namun, tidak banyak area permukaan di samping sampan untuk menulis program Anda, jadi Anda harus membuat program sesingkat mungkin.

Sungai dapat direpresentasikan sebagai grid 8 x 16. Kami akan label kolom dengan angka 0untuk 7dan baris dengan nomor 0untuk 15.

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

Atas: Sungai yang tenang dan biasa-biasa saja tanpa penghalang. Secara alami, ini bukan sungai tempat Anda berada.

Anda mulai pada koordinat (4, 0) dan dari sana bergerak tak terkendali ke atas sungai (yaitu vektor (0,1)) sampai Anda menabrak batu (diwakili oleh odalam contoh-contoh ini). Ketika Anda menabrak batu, Anda akan memiliki peluang 55% untuk bergerak melewati batu ke kiri (yaitu vektor (-1,1)) dan 45% peluang untuk bergerak melewati batu ke kanan (yaitu vektor (1,1)). Jika sampan ada di kolom paling kiri atau kanan, sampan akan selalu bergerak ke tengah. Jika tidak ada batu, itu akan bergerak lurus ke atas.

        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

Atas: Rute yang mungkin diambil kano, direpresentasikan menggunakan karakter x

Mengingat peta sungai, tulislah sebuah program yang akan menampilkan kemungkinan sampan di kolom yang diberikan.

Terima input dengan metode apa pun yang sesuai untuk program Anda (mis. STDIN, argumen baris perintah raw_input(), membaca dari file, dll). Bagian pertama dari input adalah bilangan bulat tunggal dari 0 hingga 7, mewakili kolom yang akan dicari probabilitas programnya. Berikut ini adalah daftar tupel dalam bentuk yang x,ymewakili posisi batu.

Sebuah contoh:

Memasukkan:

4 4,1 5,5 3,5

Ini akan menunjukkan sungai dengan batu di posisi (4,1), (5,5), dan (3,5), dan menanyakan kemungkinan sampan berakhir di kolom ke-4.

Keluaran:

0.495

Perhatikan bahwa dalam contoh ini, posisi batuan simetris, yang memungkinkan masalah diselesaikan dengan distribusi binomial. Ini tidak selalu terjadi!

Selain itu, sungai akan selalu bisa dilintasi. Artinya, tidak akan pernah ada dua batu yang diposisikan berdekatan satu sama lain secara horizontal. Lihat komentar Glenn untuk contoh kasus yang tidak mungkin.

Ini adalah kode golf, sehingga jumlah karakter yang paling rendah menang. Jangan ragu untuk bertanya di komentar jika spesifikasinya tidak jelas.

Absinth
sumber
8
Bagian ironisnya adalah bahwa program ini tidak benar-benar membantu siapa pun bertahan hidup dari jeram. Ini memberitahu mereka bagaimana mungkin itu adalah mereka akan bertahan hidup sekalipun.
absinthe
1
Apa yang terjadi ketika dua batu atau lebih saling berdampingan dalam satu baris? misalnya, jika peta "0 1,0 1,1", sampan akan menabrak batu di 1,1. (a) kondisi tidak diizinkan, atau (b) kemungkinan menyelesaikan kursus adalah 0.
Glenn Randers-Pehrson
1
Ah, baiklah. Maaf, saya melewatkan bagian itu.
Gagang Pintu
3
Pikiran terakhir: "Mungkin membangun kano yang bisa diprogram bukanlah solusi terbaik untuk masalah menggunakan dayung peledak."
Kai
2
Saya ingin melihat seperti apa saat dayung meledak.
Robbie Wxyz

Jawaban:

4

GolfScript, 105 karakter

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

Versi GolfScript yang menjadi lebih panjang dari yang dimaksudkan - tetapi setiap upaya dengan pendekatan yang berbeda bahkan lebih lama. Masukan harus diberikan pada STDIN.

Contoh:

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

Kode beranotasi:

# 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?*
Howard
sumber
11

Ruby, 204 191 172 karakter

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]

Itu secara rekursif mensimulasikan semua hasil yang mungkin sambil melacak probabilitas hasil masing-masing individu, kemudian menambahkan kemungkinan itu ke counter kumulatif kapan y == 15.

Trik mewah:

  • c,*r=gets.split- operator "splat" ( *) mengambil semua elemen yang tersisa gets.splitdan menempelkannya dalam rarray

  • next {something} if {condition}: pada dasarnya setara dengan

    if {condition}
        {something}
        return
    end
    

    "Ditemukan" oleh berkembang dari if condition; something; return; endke return something if conditionke break something if condition, dan kemudian saya pikir saya akan mencoba lebih pendek "loop operator" untuk melihat apakah itu akan bekerja (yang itu, tentu saja).

  • Terima kasih kepada @ MartinBüttner yang menyarankan untuk menggunakan operator ternary yang dirantai (yang akhirnya menjadi baris ketiga yang sangat besar dalam kode golf di atas) dan menghilangkan poin di atas (yang menyelamatkan 19 karakter (!)).

    Saya memang menggunakan trik yang agak mewah dengan itu, meskipun: Saya menyadari bahwa s[foo],s[bar]tidak berfungsi di Ruby untuk dua pemanggilan metode dalam satu pernyataan. Jadi pada saya pertama kali berubah ke (_=s[foo],s[bar])(variabel dummy), tapi kemudian aku sadar bahwa aku hanya bisa menambah dan membuang kembali nilai-nilai: s[foo]+s[bar]. Ini hanya berfungsi karena panggilan ke shanya akan "mengembalikan" panggilan lain ke satau nomor ( o[x]+=p), jadi saya tidak perlu khawatir untuk memeriksa nil.

  • Berbagai optimasi lainnya: palih-alih putsuntuk mencetak nomor, <1bukan ==0(karena kano tidak pernah meninggalkan sungai) dan perbandingan serupa di tempat lain, [0]*8untuk probabilitas awal karena angka-angka Ruby selalu "lewat nilai"

Tidak Terkumpul:

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]
Gagang pintu
sumber
Bukankah masih lebih pendek untuk mengumpulkan semua itu next X if Ymenjadi operator ternary bersarang? Nice find though, Anda mungkin ingin menambahkannya ke tips Ruby!
Martin Ender
@ MartinBüttner Yap, itu sebenarnya lebih pendek 19 karakter! Terima kasih, walaupun itu memiliki efek samping yang disayangkan dari garis panjang yang luar biasa: P
Doorknob
5

C # 418 364bytes

Selesaikan program C # yang mengharapkan input dari STDIN. Bekerja dengan membaca batu ke dalam array semua lokasi di sungai, secara efektif membuat peta, dan kemudian hanya melakukan 16 iterasi probabilitas bergerak di sekitar array desimal 8-lebar sebelum menghasilkan hasilnya.

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]);}}

Kode yang diformat:

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
    }
}
VisualMelon
sumber
+1 untuk menggunakan operator "go to" ( for(;j-->0;)). Anda dapat menyingkirkan beberapa karakter dengan mengganti yang terakhir C.WriteLinedengan C.Write. Juga, jika Anda menggunakan floatalih-alih, decimalAnda dapat menyimpan beberapa byte lagi.
Christoph Böhmwalder
@HackerCow praktik standar;) harus mendapatkan hasil maksimal dari for-loop Anda! Saya menggunakan decimalkarena floattidak akan tepat, tetapi desimal harus dilakukan untuk masalah ini, tetapi mungkin bisa lolos begitu saja seperti yang Anda katakan. Saya akan dimasukkan ke dalam C.Writejika saya berhasil golf ini lebih jauh karena mungkin lebih dekat dengan spesifikasi daripada C.WriteLinekarena saya tidak berpikir 4 byte memerlukan suntingan untuk program ukuran ini;)
VisualMelon
2

Haskell, 256 byte

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

Ini adalah versi yang sangat tidak digunduli bersama dengan beberapa trik yang digunakan:

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)

Trik terakhir yang saya gunakan adalah mencatat bahwa Anda dapat bertindak seolah-olah batu dalam satu baris sebenarnya dipisahkan oleh jumlah yang sangat kecil. Dengan kata lain, Anda dapat menerapkan transformator distribusi probabilitas untuk setiap batu pada baris yang sama secara berurutan dan dalam urutan apa pun yang Anda inginkan, daripada menerapkan semuanya secara bersamaan. Ini hanya berhasil karena masalah ini melarang dua batu yang berdekatan secara horizontal.

Jadi program mengubah lokasi masing-masing batu menjadi trafo distribusi probabilitas, dipesan oleh koordinat y batu Transformer kemudian dirantai secara berurutan dan diterapkan pada distribusi probabilitas awal. Dan itu dia!

Matt Noonan
sumber
2

Perl 169 Bytes

Baca dari 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{$&}

Cukup lurus ke depan, secara implisit menggunakan kolom -1 dan 8 untuk menghaluskan kasus perbatasan. Probabilitas dapat dengan aman disebarkan ke setiap level berikutnya karena tidak ada batu yang berdekatan, sehingga satu run sudah mencukupi.

Thaylon
sumber
2

PHP, 358

Menggunakan kekuatan otak untuk menentukan jalur yang mungkin dan kemungkinannya sulit, dan mungkin akan membutuhkan lebih banyak kode daripada hanya mensimulasikan 1.000.000 kecelakaan kano. Oh, kemanusiaan!

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);
}

Contoh:

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

Golf:

<? 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);

Versi ini tidak melakukan pencetakan cantik dan menampilkan probabilitas mengambang dari pendaratan kano di posisi yang ditentukan.

# php river_golf.php "4 4,1 5,5 3,3 6,2 9,4 12,3 13,5"
0.4952
Sammitch
sumber
Saya pikir format input di sini sedikit mati, misalnya river.php harus memberikan 0,561375 pada "5 4,4 1,5 5,3 3,6 2,9 4,12 3,13"
Matt Noonan
@MattNoonan itu hari yang kasar kemarin. Aku seharusnya bisa memperbaikinya ...
Sammitch
2

PHP, 274

Saya tidak bisa membaca / menulis GolfScript untuk menyelamatkan hidup saya, tetapi melirik pengajuan @ Howard menunjuk saya ke arah yang lebih baik daripada hanya mensimulasikan 1 juta kecelakaan kano.

Dimulai dengan serangkaian probabilitas untuk posisi awal, kita dapat dengan mudah membagi angka-angka itu setiap kali batu ditemukan.

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);
}

Contoh Output:

# 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

Golf:

<? $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];

Contoh dijalankan:

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

Haskell, 237

Saya hanya berharap sampan dilengkapi dengan ghc yang diinstal ...

Trik dengan daftar tak terbatas dicuri dari Matt Noonan, pujian kepadanya!

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

Saya harap logika saya benar, tetapi contoh Matt "5 4,4 1,5 5,3 3,6 2,9 4,12 3,13"menghasilkan 0.5613750000000001dan contoh OP "4 4,1 5,5 3,5"menghasilkan0.49500000000000005 , yang tampaknya benar terlepas dari beberapa kesalahan floating point.

Ini dia sedang beraksi:

>>> echo 5 4,4 1,5 5,3 3,6 2,9 4,12 3,13 | codegolf
0.5613750000000001
Flonk
sumber