激流独木舟


28

您正在划一条独木舟在一条相当快的激流河上。突然,您的船桨爆炸了,您发现自己处在危险的境地,没有任何船桨,使河流急速驶下。幸运的是,您仍然具有编程技能,因此您决定在独木舟旁边雕刻一个程序,以帮助您在急流中生存。但是,独木舟侧面没有太多的表面可用来编写程序,因此您必须使程序尽可能短。

河流可以表示为8 x 16的网格。我们将在列上标记数字07,在行上标记数字015

        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%的机会越过岩石的左侧(即vector (-1,1)),而有45%的机会就越过岩石的右侧(即vector (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)处有岩石的河流,并询问独木舟在第四列结束的可能性。

输出:

0.495

请注意,在此示例中,岩石的位置是对称的,从而可以通过二项式分布来解决问题。并非总是如此!

另外,河流将永远是可穿越的。也就是说,永远不会有两个水平相邻的岩石。见格伦的评论不可能案例的示例,。

这是代码高尔夫,因此最少的字符获胜。如果规范不明确,请随时在评论中提问。


8
具有讽刺意味的是,该程序实际上并不能帮助任何人在急流中生存。它告诉他们这是怎么可能,他们会生存下去,虽然。
苦艾酒

1
当两个或多个岩石连续并排时会发生什么?例如,如果地图为“ 0 1,0 1,1”,则独木舟将在1,1撞入岩石。(a)中的条件是不允许的,或者在完成过程(b)的概率是0。
格伦兰德斯·皮尔逊

1
喔好吧。抱歉,我错过了那部分。
门把手

3
最后的想法:“也许建造一个可编程的独木舟并不是解决使用爆炸桨的最佳方法。”
2014年

2
我想看看桨爆炸时的样子。
罗比·维克兹

Answers:


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版本变得比预期的要长得多-但是用不同方法进行的每次尝试甚至更长。输入必须在STD​​IN上给出。

例:

> 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 conditionbreak 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因为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到嵌套三元运算符中,它还会更短吗?不错的发现,您可能希望将其添加到Ruby技巧中!
马丁·恩德

@MartinBüttner是的,实际上短了19个字符!谢谢,尽管它确实有一个荒谬的漫长行的不幸副作用:P
门把手

5

C# 418 364bytes

完整的C#程序,需要STDIN的输入。通过将岩石读取到河流中所有位置的数组中进行工作,有效地创建了地图,然后在输出结果之前,它仅围绕8宽度的十进制数组执行了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
    }
}

+1,用于使用“转到”运算符(for(;j-->0;))。您可以通过,虽然将最后摆脱几个字符的C.WriteLine通过C.Write。另外,如果您使用float而不是,则decimal可以节省更多的字节。
ChristophBöhmwalder2014年

@HackerCow标准实践;)可以充分利用您的for循环!我使用它decimal是因为float不够精确,但是这些问题应该用十进制表示,但是您可能会说得过去。C.Write如果我能继续打高尔夫球,我会加进去,因为它可能更接近规格,而不是C.WriteLine我认为4个字节不能保证可以编辑此大小的程序;)
VisualMelon 2014年

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

用脑力来确定可能的路径及其概率很困难,并且可能比简单地模拟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
Matt Noonan

@MattNoonan昨天真是艰难的一天。我应该能够解决这个问题……
Sammitch 2014年

2

PHP,274

我无法读/写GolfScript来挽救生命,但是浏览@Howard提交的内容为我提供了比仅模拟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"收益率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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.