Canoeing estremo di Whitewater


28

Stai remando una canoa lungo un fiume di acque bianche abbastanza veloce. All'improvviso, le tue pagaie esplodono e ti ritrovi in ​​una situazione pericolosa che sfreccia lungo un fiume veloce senza pagaie. Fortunatamente, hai ancora le tue capacità di programmazione, quindi decidi di scolpire un programma sul lato della tua canoa per aiutarti a sopravvivere alle rapide. Tuttavia, non c'è molta superficie sul lato della canoa con cui scrivere il programma, quindi è necessario rendere il programma il più breve possibile.

Il fiume può essere rappresentato come una griglia 8 per 16. Etichettiamo le colonne con i numeri 0a 7e le righe con i numeri 0a 15.

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

Sopra: un fiume completamente calmo e normale senza ostacoli. Naturalmente, questo non è il fiume in cui ti trovi.

Inizi alle coordinate (4, 0) e da lì ti muovi in ​​modo incontrollato sul fiume (cioè il vettore (0,1)) fino a colpire una roccia (rappresentata da una oin questi esempi). Quando colpisci una roccia, avrai una probabilità del 55% di spostarti oltre la roccia a sinistra (cioè il vettore (-1,1)) e una probabilità del 45% di spostarti oltre la roccia a destra (cioè il vettore (1,1)). Se la canoa si trova sulle colonne più a sinistra o a destra, si sposterà sempre verso il centro. Se non ci sono rocce, si sposterà direttamente verso l'alto.

        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

Sopra: un possibile percorso che la canoa potrebbe prendere, rappresentato usando il personaggio x

Data la mappa del fiume, scrivi un programma che fornirà la probabilità che la canoa finisca in una data colonna.

Accetta l'input in qualunque metodo sia conveniente per il tuo programma (es. STDIN, argomento della riga di comando raw_input(), lettura da un file, ecc.). La prima parte dell'input è un singolo intero compreso tra 0 e 7, che rappresenta la colonna per la quale il programma troverà la probabilità. Di seguito è riportato un elenco di tuple nella forma che x,yrappresenta la posizione delle pietre.

Un esempio:

Ingresso:

4 4,1 5,5 3,5

Ciò indicherebbe un fiume con rocce nelle posizioni (4,1), (5,5) e (3,5), e chiede la probabilità che la canoa finisca alla quarta colonna.

Produzione:

0.495

Si noti che in questo esempio, le posizioni delle rocce erano simmetriche, consentendo di risolvere il problema con una distribuzione binomiale. Questo non sarà sempre il caso!

Inoltre, il fiume sarà sempre attraversabile. Cioè, non ci saranno mai due rocce posizionate l'una accanto all'altra in senso orizzontale. Vedi il commento di Glenn per un esempio di caso impossibile.

Questo è il golf del codice, quindi vince il numero più basso di caratteri. Sentiti libero di porre domande nei commenti se le specifiche non sono chiare.


8
La parte ironica è che il programma in realtà non aiuta nessuno a sopravvivere alle rapide. Non dire loro quanto è probabile faranno a sopravvivere comunque.
assenzio

1
Cosa succede quando due o più rocce sono affiancate di fila? ad esempio, se la mappa è "0 1,0 1,1" la canoa si schianterebbe contro la roccia a 1,1. (a) condizione non consentita o (b) probabilità di completare il corso è 0.
Glenn Randers-Pehrson

1
Ah ok. Scusa, ho perso quella parte.
Maniglia della porta

3
Considerazioni finali: "Forse costruire una canoa programmabile non era la soluzione migliore al problema dell'uso di palette esplosive".
Kai,

2
Voglio vedere come appare quando esplode una pagaia.
Robbie Wxyz,

Risposte:


4

GolfScript, 105 caratteri

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

Una versione GolfScript che è diventata molto più lunga del previsto, ma ogni tentativo con un approccio diverso è stato anche più lungo. L'ingresso deve essere dato su STDIN.

Esempio:

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

Codice annotato:

# 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

Rubino, 204 191 172 caratteri

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]

Simula in modo ricorsivo tutti i possibili esiti mentre tiene traccia della probabilità di ogni singolo risultato, quindi aggiunge tale probabilità a un contatore cumulativo quando y == 15.

Trucchi di fantasia:

  • c,*r=gets.split- l'operatore "splat" ( *) prende tutti gli elementi rimanenti di gets.splite li inserisce rnell'array

  • next {something} if {condition}: sostanzialmente equivalente a

    if {condition}
        {something}
        return
    end
    

    "Scoperto" evolvendo da if condition; something; return; enda return something if conditiona break something if condition, e poi ho pensato che avrei provato un "operatore a circuito" più corto per vedere se avrebbe funzionato (cosa che ha fatto, ovviamente).

  • Grazie a @ MartinBüttner per aver suggerito di usare operatori ternari incatenati (che alla fine sono diventati l'enorme terza riga nel codice golf sopra) e di eliminare il punto sopra (che ha salvato 19 (!) Caratteri).

    Ho usato un trucco un po 'elegante con quelli, però: mi sono reso conto che s[foo],s[bar]non funziona in Ruby per due chiamate di metodo in un'istruzione. Così in un primo momento ho cambiato in (_=s[foo],s[bar])(una variabile dummy), ma poi ho capito che potevo solo aggiungere e buttare via i valori di ritorno: s[foo]+s[bar]. Questo funziona solo perché le chiamate a s"restituiranno" solo altre chiamate so un numero ( o[x]+=p), quindi non devo preoccuparmi di cercare nil.

  • Altre varie ottimizzazioni: panziché putsper la stampa dei numeri, <1anziché ==0(poiché la canoa non lascia mai il fiume) e confronti simili altrove, [0]*8per le probabilità iniziali poiché i numeri di Ruby sono sempre "pass per valore"

Ungolfed:

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]

Non sarebbe ancora più breve riunirli tutti next X if Yin operatori ternari nidificati? Bella scoperta però, potresti volerlo aggiungere ai suggerimenti di Ruby!
Martin Ender,

@ MartinBüttner Sì, è in realtà un enorme 19 caratteri in meno! Grazie, anche se ha lo sfortunato effetto collaterale di una linea ridicolmente lunga: P
Maniglia della porta

5

C # 418 364 byte

Programma completo C # in attesa di input da STDIN. Funziona leggendo le rocce in una matrice di tutte le posizioni nel fiume, creando in modo efficace una mappa, quindi esegue solo 16 iterazioni di probabilità di spostamento attorno a una matrice decimale di 8 larghezze prima di produrre il risultato.

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

Codice formattato:

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

+1 per l'utilizzo dell'operatore "vai a" ( for(;j-->0;)). Puoi sbarazzarti di un paio di personaggi sostituendo l'ultimo C.WriteLinecon C.Write. Inoltre, se si utilizza al floatposto di decimalè possibile salvare un paio di byte in più.
Christoph Böhmwalder,

La pratica standard di @HackerCow;) è riuscita a ottenere il massimo dai tuoi for-loop! Sto usando decimalperché floatnon sarà preciso, ma il decimale dovrebbe fare per questi problemi, ma probabilmente potrebbe cavarsela come dici tu. Inserirò C.Writese riesco a giocare a golf ulteriormente poiché è probabilmente più vicino alle specifiche di C.WriteLinequanto non penso che 4 byte meriti una modifica per questo programma di dimensioni;)
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

Ecco una versione molto non golfata con alcuni trucchi che sono stati utilizzati:

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)

L'ultimo trucco che ho usato è stato notare che puoi agire come se le rocce in una singola fila fossero effettivamente separate da una quantità infinitesimale. In altre parole, puoi applicare il trasformatore di distribuzione di probabilità per ogni roccia nella stessa riga in sequenza e nell'ordine desiderato, anziché applicarle tutte contemporaneamente. Questo funziona solo perché il problema non consente due rocce adiacenti orizzontalmente.

Quindi il programma trasforma la posizione di ogni roccia in un trasformatore di distribuzione di probabilità, ordinato dalla coordinata y della roccia. I trasformatori vengono quindi incatenati in ordine e applicati alla distribuzione di probabilità iniziale. E quello è quello!


2

Perl 169 byte

Legge da 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{$&}

Abbastanza semplice, usa implicitamente le colonne -1 e 8 per appianare i casi di confine. Le probabilità possono essere tranquillamente propagate ad ogni livello successivo perché non ci sono pietre adiacenti, quindi è sufficiente una sola corsa.


2

PHP, 358

L'uso della forza di cervello per determinare i possibili percorsi e le loro probabilità è difficile e probabilmente richiederebbe più codice rispetto alla semplice simulazione di 1.000.000 di incidenti in canoa. Oh l'umanità!

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

Esempio:

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

golfed:

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

Questa versione non esegue alcuna stampa e genera la probabilità di galleggiamento dell'atterraggio della canoa nella posizione specificata.

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

Penso che il formato di input qui sia leggermente off, ad esempio river.php dovrebbe dare 0,561375 su "5 4,4 1,5 5,3 3,6 2,9 4,12 3,13"
Matt Noonan,

@MattNoonan ieri è stata una giornata difficile. Dovrei essere in grado di risolvere il problema ...
Sammitch,

2

PHP, 274

Non riesco a leggere / scrivere GolfScript per salvarmi la vita, ma dare un'occhiata alla presentazione di @ Howard mi ha indicato una direzione migliore rispetto alla semplice simulazione di 1 milione di incidenti in canoa.

A partire da una serie di probabilità per le posizioni di partenza, possiamo semplicemente dividere quei numeri ogni volta che si incontra una roccia.

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

Esempio di 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

golfed:

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

Esempio di esecuzione:

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

1

Haskell, 237

Spero solo che la canoa arrivi con ghc installato ...

Il trucco con la lista infinita viene rubato da Matt Noonan, complimenti a lui!

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

Spero di aver capito bene la logica, ma l'esempio di Matt "5 4,4 1,5 5,3 3,6 2,9 4,12 3,13"produce 0.5613750000000001e l'esempio di OP "4 4,1 5,5 3,5"produce 0.49500000000000005, che sembra essere corretto a parte alcuni errori in virgola mobile.

Eccolo in azione:

>>> echo 5 4,4 1,5 5,3 3,6 2,9 4,12 3,13 | codegolf
0.5613750000000001
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.