Aşırı Whitewater Kano


28

Oldukça hızlı bir beyaz su nehrinde bir kanoyla kürek çekiyorsunuz. Birden kürekleriniz patlar ve kendinizi herhangi bir kürek çekmeden hızlı bir şekilde nehirden aşağı atlayan tehlikeli bir durumda bulursunuz. Neyse ki, hala programlama becerileriniz var, bu yüzden kanolarda hayatta kalmanıza yardımcı olmak için kano tarafında bir program açmaya karar veriyorsunuz. Bununla birlikte, kano tarafında programınızı yazmak için çok fazla yüzey alanı yoktur, bu nedenle programı mümkün olduğunca kısa tutmalısınız.

Nehir 8'e 16'lık bir ızgara olarak temsil edilebilir. Biz sayılarla sütunları etiketleyecektir 0için 7ve numaraları ile satır 0için 15.

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

Yukarıda: Engelsiz, tamamen sakin, sıradan bir nehir. Doğal olarak, bu bulunduğunuz nehir değil.

Koordinattan (4, 0) başlıyorsunuz ve oradan nehre (yani vektör (0,1)) kadar kontrol edilemeyen bir kayaya ( obu örneklerde temsil edilen) temsil edilene kadar kontrol edilemiyor . Bir kayaya çarptığınızda, kayadan sola doğru hareket etme şansı (yani vektör (-1,1)) ve% 45'i kayadan sağa doğru hareket etme şansı (yani vektör ) elde edersiniz (1,1). Kano en sol veya sağ sütunlarda ise, daima merkeze doğru hareket eder. Kaya yoksa, dümdüz yukarı doğru hareket eder.

        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

Üstte: Kano karakterini kullanarak gösterilen olası bir rota x

Nehir haritası göz önüne alındığında, kano bitirme olasılığını belirli bir sütuna verebilecek bir program yazın.

Hangi yöntemin programınız için uygun olduğunu kabul edin (örn. STDIN, komut satırı argümanı, raw_input()bir dosyadan okuma, vb.). Girişin ilk bölümü 0'dan 7'ye kadar olan tek bir tamsayıdır, programın olasılık bulacağı sütunu temsil eder. Bunu takiben x,y, taşların pozisyonunu temsil eden formda bulunan bir listeler listelenmiştir .

Bir örnek:

Giriş:

4 4,1 5,5 3,5

Bu, (4,1), (5,5) ve (3,5) konumlarında kayalar bulunan bir nehri gösterir ve 4. sütunda biten kano olasılığını sorar.

Çıktı:

0.495

Bu örnekte, kayaların konumlarının simetrik olduğunu ve sorunun binom dağılımında çözülmesini sağladığını unutmayın. Bu her zaman böyle olmayacak!

Ayrıca, nehir her zaman geçilebilir olacak. Yani, yatay olarak birbirine bitişik yerleştirilmiş iki kaya asla olmayacak. İmkansız bir dava örneği için Glenn'in yorumuna bakınız .

Bu kod golf, yani en düşük karakter sayısı kazanır. Şartname açık değilse yorumlarda soru sormaktan çekinmeyin.


8
İronik kısım, programın kimsenin hayatta kalma ihtimalinden kurtulmasına yardım etmemesi. Bu onlar olsa hayatta edeceğiz gelme ihtimaline onlara gelmez.
pelin otu

1
İki veya daha fazla taş yan yana yan yana olduğunda ne olur? Örneğin, harita "0 1,0 1,1" ise, kano 1,1'de kayaya çarpacaktır. (a) koşullara izin verilmez veya (b) kursu tamamlama olasılığı 0'dır.
Glenn Randers-Pehrson 23: 24

1
Ah tamam. Üzgünüm, bu kısmı özledim.
Doorknob

3
Son düşünceler: "Belki de programlanabilir bir kano inşa etmek, patlayıcı kürekler kullanma sorununa en iyi çözüm değildi."
Kai,

2
Bir kürek patladığında neye benzediğini görmek istiyorum.
Robbie Wxyz,

Yanıtlar:


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?*

Amaçlandığından daha uzun süren bir GolfScript sürümü - ancak farklı bir yaklaşıma sahip her deneme daha da uzundu. Giriş STDIN'de verilmelidir.

Örnek:

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

Açıklamalı kod:

# 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

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]

Her bir sonucun olasılığını takip ederken tüm olası sonuçları tekrarlı olarak simüle eder, ardından bu olasılığı bir kümülatif sayaca ekler y == 15.

Süslü püf noktaları:

  • c,*r=gets.split- "splat" operatörü ( *) kalan tüm elemanları alır gets.splitve rdiziye yapıştırır.

  • next {something} if {condition}: esasen eşdeğer

    if {condition}
        {something}
        return
    end
    

    'Dan' if condition; something; return; endya return something if conditiondoğru gelişerek “keşfedildi” break something if conditionve sonra işe yarayıp yaramadığını görmek için daha kısa bir “loop operatörü” deneyeceğimi düşündüm.

  • Zincirli üçlü operatörlerin kullanılmasını öneren (yukarıdaki golf kodunda muazzam üçüncü sıra haline geldi) ve yukarıdaki (19 (!) Karakterini kazandıran) karakteri ortadan kaldıran) için @ MartinBüttner'e teşekkür ederiz.

    Bununla birlikte, biraz süslü bir numara kullandım: s[foo],s[bar]Ruby'nin bir açıklamada iki yöntem çağrısı için işe yaramadığını anladım . Yani ilk I'de olarak değiştirdim (_=s[foo],s[bar])(kukla değişken), ama sonra ben sadece ekleyebilir ve dönüş değerleri atabilirm gerçekleşmiştir: s[foo]+s[bar]. Bu sadece işe yarar, çünkü çağrılar syalnızca diğer çağrıları sveya bir sayıyı ( o[x]+=p) “geri getirir” , bu yüzden kontrol etme konusunda endişelenmeme gerek kalmaz nil.

  • Diğer çeşitli optimizasyonlar: pyerine puts, sayıları yazdırmak için <1yerine ==0başka bir yerde ve benzer karşılaştırmalar (kano nehir asla terk etmez beri) [0]*8her zaman "değerine geçmek" olan Ruby'nin numaraları gibi ilk olasılıklar için

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]

Bunların next X if Yhepsini iç içe üçlü operatörlere toplamak daha kısa olmaz mıydı ? Güzel bul olsa da, Ruby ipuçlarına eklemek isteyebilirsiniz!
Martin Ender

@ MartinBüttner Evet, aslında bir kuyruklu 19 karakter daha kısa! Teşekkürler, gülünç derecede uzun bir çizginin talihsiz yan etkisine sahip olmasına rağmen: P
Doorknob

5

C # 418 364bayt

STDIN'den giriş bekleyen C # programını tamamlayın. Kayaları nehirdeki tüm yerlerin bir dizisine okuyarak çalışır, etkili bir harita oluşturur ve ardından sonucu çıkarmadan önce 8 genişlik ondalık bir dizinin etrafında 16 yineleme hareketli olasılık gerçekleştirir.

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

Biçimlendirilmiş kod:

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

"Gider" işlecini kullanmak için +1 ( for(;j-->0;)). Yine de son değiştirerek karakterlerin bir çift kurtulmuş oluruz C.WriteLinetarafından C.Write. Ayrıca, floatyerine kullanırsanız decimalbirkaç bayttan tasarruf edebilirsiniz.
Christoph Böhmwalder

@HackerCow standart uygulama;) for-loop'larınızdan en iyi şekilde yararlanmanız! Kullanıyorum decimalçünkü floatkesin olmayacak, ancak ondalık bu sorunlar için yapmalı, ancak muhtemelen dediğiniz gibi kurtulabildi. Ben de koyacağım C.Writedaha spec muhtemelen daha yakın bu başka ben golfe yönetmek durumunda C.WriteLine); ı 4 bayt emri bu boyuttaki program için bir düzenleme sanmıyorum
VisualMelon

2

Haskell, 256 bayt

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

İşte kullanılan bazı hileler ile birlikte çok ungolfed versiyonu:

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)

Kullandığım son numara, tek bir sıradaki kayaların aslında sonsuz küçük bir miktarla ayrılmış gibi davranabileceğini not etmekti. Başka bir deyişle, her kaya için olasılık dağılım trafosunu, hepsini aynı anda uygulamak yerine, sırayla ve istediğiniz sırada aynı sıra ile uygulayabilirsiniz. Bu sadece işe yarıyor çünkü sorun yatay olarak bitişik iki taşı engellemiyor.

Böylece program, her bir kayanın konumunu, kayanın y koordinatı tarafından sıralanan bir olasılık dağıtım trafosuna dönüştürür. Transformatörler daha sonra sırayla zincirlenir ve ilk olasılık dağılımına uygulanır. Ve bu o!


2

Perl 169 Bayt

STDIN'den okur.

$_=<>;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{$&}

Oldukça dümdüz, sınır durumlarını düzeltmek için örtülü olarak -1 ve 8 sütunlarını kullanıyor. Olasılıklar güvenli bir şekilde her iki seviyeye de yayılabilir, çünkü herhangi bir bitişik taş bulunmadığından tek bir işlem yeterlidir.


2

PHP, 358

Olası yolları ve olasılıklarını belirlemek için beyin gücünü kullanmak zordur ve muhtemelen sadece 1.000.000 kano kazasını taklit etmekten daha fazla kod gerektirir. Ah insanlık!

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

Örnek:

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

Bu sürüm, herhangi bir güzel baskı yapmaz ve kano inişinin belirtilen pozisyonda yüzdürme olasılığını verir.

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

Buradaki giriş biçiminin biraz kapalı olduğunu düşünüyorum, örneğin river.php "5 4,4 1,5 5,3 3,6 2,9 4,12 3,13" yerine 0,561375 vermelidir
Matt Noonan

@MattNoonan dün zor bir gündü. Bunu düzeltebilmeliyim ...
Sammitch

2

PHP, 274

Hayatımı kurtarmak için GolfScript okuyamıyorum / yazamıyorum, ancak @ Howard'ın gönderimini incelemek, beni sadece 1 milyon kano kazasını taklit etmekten daha iyi bir yöne işaret etti.

Başlama pozisyonları için bir dizi olasılıkla başlayarak, bir kayaya her rastlandığında bu sayıları bölebiliriz.

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

Örnek çıktı:

# 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];

Örnek çalışma:

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

1

Haskell, 237

Umarım kano ghc ile birlikte gelir.

Sonsuz listedeki numara, Noonan'dan çalındı, şeref ona!

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

Umarım mantığımı doğru anlamışımdır, ancak Matt'in örnek "5 4,4 1,5 5,3 3,6 2,9 4,12 3,13"verimi 0.5613750000000001ve bazı kayan nokta hatalarından ayrı olarak doğru görünen OP'nin örnek "4 4,1 5,5 3,5"verimi 0.49500000000000005.

İşte eylemde:

>>> echo 5 4,4 1,5 5,3 3,6 2,9 4,12 3,13 | codegolf
0.5613750000000001
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.