Екстремальне біловодне каное


28

Ви катаєтеся на каное по досить швидкій річці біловодних. Раптом ваші весла вибухають, і ви потрапляєте в небезпечну ситуацію, що впадає в річку швидкої, без жодних весла. На щастя, у вас все ще є навички програмування, тому ви вирішили вирізати програму на боці свого каное, щоб допомогти вам пережити пороги. Однак з боку каное не має великої площі поверхні для написання вашої програми, тому ви повинні зробити програму якомога коротшою.

Річка може бути представлена ​​як сітка 8 на 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

Зауважимо, що в цьому прикладі положення гірських порід були симетричними, що дозволяло вирішити задачу з біноміальним розподілом. Це не завжди так!

Також річка завжди буде пересічною. Тобто ніколи не буде двох скель, розташованих поруч один з одним по горизонталі. Дивіться коментар Глена на прикладі неможливої ​​справи.

Це код гольфу, тому виграє найменша кількість символів. Ви можете задавати питання в коментарях, якщо специфікація не є чіткою.


8
Іронічна частина полягає в тому, що програма насправді нікому не допомагає пережити пороги. Це сказати їм , наскільки велика ймовірність, що вони будуть виживати , хоча.
абсент

1
Що відбувається, коли дві або більше скель стоять поруч поруч? наприклад, якщо карта "0 1,0 1,1", каное врізається в скелю на рівні 1,1. (а) умова заборонена, або (б) ймовірність пройти курс становить 0.
Гленн Рендерс-Персон

1
Ну, тоді добре. Вибачте, я пропустив цю частину.
Doorknob

3
Заключні думки: "Можливо, побудова програмованого каное не було найкращим вирішенням проблеми використання вибухонебезпечних весла".
Кай

2
Я хочу побачити, як це виглядає, коли весло вибухає.
Роббі Вікс

Відповіді:


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; endдо return something if conditionдо break something if condition, і тоді я подумав, що спробую коротший "оператор циклу", щоб побачити, чи буде він працювати (що, звичайно, робив).

  • Завдяки @ MartinBüttner за пропозицію використовувати приковані потрійні оператори (які в кінцевому підсумку стали величезним третім рядком у коді для гольфу вище) та усунув вищезгадану точку (яка зберегла 19 (!) Символів).

    Я все ж використовував дещо химерний трюк з тими: я зрозумів, що s[foo],s[bar]в Ruby не працює два виклики методу в одному операторі. Так що спочатку я змінив його (_=s[foo],s[bar])(змінний фіктивний), але потім я зрозумів , що я міг би просто додати і викинути повернені значення: s[foo]+s[bar]. Це працює лише тому, що дзвінки на s"коли-небудь" повертатимуть інші дзвінки на sабо номер ( o[x]+=p), тому мені не доведеться турбуватися про перевірку nil.

  • Інші різні оптимізації: pзамість того, putsщоб друкувати номери, <1а не ==0(оскільки каное ніколи не залишає річку) та подібні порівняння в інших місцях, [0]*8для початкових ймовірностей, оскільки цифри Рубі завжди "передаються за значенням"

Безголівки:

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у вкладені потрійні оператори? Хоча приємна знахідка, ви можете додати її до порад Ruby!
Мартін Ендер

@ MartinBüttner Так, це насправді колосальних на 19 символів коротше! Дякую, хоча у нього є нещасний побічний ефект смішно довгого рядка: P
Doorknob

5

C # 418 364 байт

Повна програма C #, що очікує введення від STDIN. Працює, читаючи скелі в масиві всіх локацій в річці, ефективно створюючи карту, а потім вона просто виконує 16 ітерацій переміщення ймовірностей навколо десяткового масиву 8-ширини перед виведенням результату.

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

+1 для використання оператора "йде в" ( for(;j-->0;)). Ви можете позбутися пари символів, хоча замінивши останнє C.WriteLineна C.Write. Крім того, якщо ви використовуєте floatзамість decimalвас, ви можете зберегти ще пару байтів.
Крістоф Бьомвальдер

@HackerCow стандартна практика;) отримала максимум користі від своїх циклів! Я використовую, decimalтому що floatне буде точним, але для цих проблем слід робити десятковий характер, але, ймовірно, міг би піти з цього, як ви кажете. Я вступлю, C.Writeякщо мені вдасться займатись цим у гольф, оскільки це, мабуть, ближче до специфікації, ніж так, C.WriteLineяк я не думаю, що 4 байти
вимагають

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)

Останній трюк, який я використав, - зазначити, що ви можете діяти так, ніби породи в одному ряді насправді розділені якоюсь нескінченною малою кількістю. Іншими словами, ви можете застосовувати трансформатор розподілу ймовірностей для кожної породи в одному рядку послідовно і в будь-якому порядку, який ви хочете, а не застосовувати їх одночасно. Це працює лише тому, що проблема усуває дві горизонтально сусідні скелі.

Таким чином програма перетворює місце кожної скелі в трансформатор розподілу ймовірностей, упорядкований координатою 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 для згладжування кордонів. Ймовірності можна сміливо поширювати на кожен наступний рівень, оскільки немає сусідніх каменів, тому одного пробігу достатньо.


2

PHP, 358

Використовувати мозкові сили для визначення можливих шляхів та їх ймовірностей важко, і, ймовірно, знадобиться більше коду, ніж просто імітувати 1000 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 повинен дати 0,561375 на "5 4,4 1,5 5,3 3,6 2,9 4,12 3,13"
Метт Нунан

@MattNoonan це був вчора важкий день. Я мав би змогу це виправити ...
Саммітч,

2

PHP, 274

Я не можу читати / писати GolfScript, щоб врятувати своє життя, але погляд на подання @ Говарда вказував на мене в кращому напрямку, ніж просто імітувати 1 мільйон аварій на каное.

Починаючи з масиву ймовірностей для початкових позицій, ми можемо просто розділити ці числа щоразу, коли трапляється скеля.

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

Трюк з нескінченним списком викрадений у Метта Нунана, кудо йому!

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

Я сподіваюся , що я отримав логіку правильно, але Метт приклад "5 4,4 1,5 5,3 3,6 2,9 4,12 3,13"врожайності 0.5613750000000001і приклад Op по "4 4,1 5,5 3,5"врожайності 0.49500000000000005, що видається правильним за винятком деяких помилок з плаваючою точкою.

Ось це в дії:

>>> echo 5 4,4 1,5 5,3 3,6 2,9 4,12 3,13 | codegolf
0.5613750000000001
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.