从数组中随机选择


19

这个挑战相当简单:
给您一个正整数(不包括0)整数数组,并且必须从该数组中选择一个随机元素。

但是这是一个变数:
选择元素的概率取决于整数的值,这意味着随着整数的变大,它被选中的概率也变大了!

给你数组[4, 1, 5]

在这种情况下,选择4的概率等于4除以数组中所有元素的总和4 / ( 4 + 1 + 5 ) = 4 / 10 =40%
选择1的概率为1 / 1010%

输入项

正整数数组。

输出量

如果使用方法,则返回所选的整数,或将其直接打印到stdout

规则

  • 这是因此任何语言中以字节为单位的最短代码都胜出。
  • 禁止出现标准漏洞。

Answers:


20

果冻,3个字节

x`X

在线尝试!

看,没有Unicode!

说明:

x`X
 `  Make monad from dyad and use same left and right arguments
x   Repeat each element of the left argument (implicit) list its respective number of times in the right argument list
  X Random element

1
您能解释一下您的代码做什么吗?:)
Ian H.

1
@IanH。这实际上是一个简单的算法,重复每个元素本身的次数,然后随机选择。
Erik the Outgolfer

16

R,25个字节

function(s)sample(s,1,,s)

在线尝试!

说明:

function(s){
 sample(x = s, size = 1, replace = FALSE, prob = s)
}

取具有重量s的大小1不更换的样品s;这些被重新缩放为概率。

要验证分布,请使用此链接


你把我击败了9个月!:D
JayCe

@JayCe呵呵,我对您的唯一优势似乎是“先行”,因为您相当高尔夫球!:-)
朱塞佩

13

Pyth,4个字节

OsmR

在这里尝试。

感谢@Jakube,使用一种非常不寻常的方法节省了一个字节。

Pyth,5个字节

Osm*]

在这里尝试!

怎么样?

#1

OsmR-完整程序。

   R-右图...
  m-...使用地图。这实际上创建了列表[[4,4,4,4],[1],[5,5,5,5,5]]。
       -...为此,应归功于Jakube!
 s-展平。
O-^的随机元素。隐式显示。

#2

Osm *]-完整程序。

  m-映射输入。
    ]-当前元素d,已包装;[d]。
   *-重复d次。
 s-展平。
O-随机元素。隐式打印结果。

1
我可以在4中做到。我应该宠坏它,还是想自己找到它?
雅库布

2
@Jakube等一下。想看看我能不能做。难道明摆着的吗?
Xcoder先生17年

1
@Jakube好吧,我放弃时会Ping。
Xcoder先生17年

1
OsmLOsmR
Jakube

1
@Jakube Ooh,非常聪明!隐式参数d,然后映射d一个范围...天才!
Erik the Outgolfer

8

CJam(9字节)

q~_]ze~mR

在线演示。这是一个完整的程序,它将在stdin上以CJam数组格式输入,并将所选元素打印到stdout。

解剖

q~   e# Read and parse input
_]z  e# Copy and transpose
e~   e# Run-length decode
mR   e# Select random element uniformly

1
一个如此强大的高尔夫,可以完成如此​​简单的任务。
Erik the Outgolfer

7

Perl 6,20个字节

感谢@Brad Gilbert b2gills,节省了1个字节。

{bag(@_ Zxx@_).pick}

在线尝试!

这需要1个列表参数。我们使用xx运算符将该列表的2个副本压缩。因此,有了@_ Zxx@_,我们得到一个列表,其中列出了元素xx时间。然后将Bag其强制到,这是一个集合,用于存储对象以及它们在集合中出现的次数。最后,我们使用pick,从该集合中选择一个随机元素,该元素将计数纳入考虑范围并执行Right Right Thing™。


可以缩短为{bag(@_ Z=>@_).pick}
Brad Gilbert b2gills

@ BradGilbertb2gills,可惜这不起作用。它由成对的袋子制成(因此不会一次出现“ 1”,两次不会出现“ 2”,但是一次就会出现“ 1 => 1”,一次也不会出现“ 2 => 2”,这不是我想要的) 。这是因为作曲家不是强迫者,如本《来临日历》所述
拉米利斯

@ BradGilbertb2gills,但是无论如何还是谢谢你,你帮助我在这里打出了一些空间以及其他挑战!
拉米利斯

我的意思是{bag(@_ Zxx@_).pick}
布拉德·吉尔伯特b2gills

啊,我明白了。为什么我没想到...:-)谢谢。
Ramillies


5

MATL8 6字节

tY"1Zr

MATL在线上尝试一下

说明

t    % Implicit input. Duplicate
Y"   % Run-length decoding
1Zr  % Randomly take one value with uniform distribution. Implicitly display




4

Java(OpenJDK 8)88 87 86 83字节

a->{int r=0,x=-1;for(int i:a)r-=i;for(r*=Math.random();r<1;)r+=a[++x];return a[x];}

在线尝试!


你能补充一个解释吗?我正在尝试理解为什么for(r*=Math.random();;)需要,或者是否需要r*=Math.random()
Ayb4btu

@ Ayb4btu如果没有for(;;)循环,则需要在后面加上第二个(从未到达的)返回语句for(int i:a)...来满足编译器的要求-这将长3个字节。
Nevay

啊,当然,您for(int i:a)就像foreach在C#中一样。我有同样的问题,但只是使用了一个for不断循环的问题。您的新答案引起了我的兴趣,我可能会尝试窃取您的一些想法。
Ayb4btu

3

J,8 7 8字节

7字节无效;一两天后返回计算机时,我会将其还原为以前的编辑。

在线尝试!

?@+/{#~

:(从数组中选择随机元素非常昂贵。

8字节

#~{~1?+/

9字节

(1?+/){#~

说明

?@+/{#~
?        Choose random number in range
  +/     Sum of the array
    {    Select that element from
     #~  The elements duplicated as many times as their value

?@+/(?@+)/; 恐怕您将不得不再次将其提高到8…
FireFly

@FireFly我应该对其进行更多测试,好捕获。
cole

3

JavaScript(ES6),50个字节

a=>a.sort((a,b)=>b-a)[Math.random()**2*a.length|0]

希望这是显而易见的,但是无论如何我都会在这里解释。它按降序对整数进行排序,然后以beta分布(1 / 2,1)随机选择一个。


我认为这不会有正确的分布。我的测试表明,与a=[4,1,5],你会得到约18%1,24%4和58% 5,这表明你会得到分布,长度3的任何输入
朱塞佩

对我来说这似乎是正确的。整数越高,概率越高。
kamoroso94年

哦,我懂了。我看错了问题。出色的解决方案,+ 1!
Giuseppe


2

PowerShell,27个字节

($args[0]|%{,$_*$_})|Random

在线尝试!

将输入$args[0]作为文字数组。循环遍历每个元素|%{...},每次迭代都构造一个新,$_$_元素数组-例如,4为此将创建一个数组@(4,4,4,4)。然后Get-Random将这些数组元素通过管道传输到其中,这些元素将以(伪)相等的概率拔出其中一个元素。因为例如@(4,1,5)为此,@(4,4,4,4,1,5,5,5,5,5)这满足了概率要求。


2

C#(.NET Core)93 89 87 76 + 18 = 94字节

a=>{int i=-1,r=new Random().Next(a.Sum());while(r>=0)r-=a[++i];return a[i];}

在线尝试!

额外的18个字节 using System.Linq;

致谢

多亏了Nevay节省了11个字节,Nevay的随机数实现更加简洁(并且int代替了double)。

脱胶

a=>{
    int i=-1,
    r=new Random().Next(a.Sum());
    while(r>=0)
        r-=a[++i];
    return a[i];
}

说明

获取一个r介于0和元素总和之间的随机数。然后在每次迭代中从中减去当前元素r。如果r小于0,则返回此元素。这个想法是,对于数组中较大的数字,随机数的一部分更大。


94个字节:a=>{int i=-1,r=new Random().Next(a.Sum());for(;r>=0;)r-=a[++i];return a[i];}
Nevay

2

Japt,7个字节

ËÆD
c ö

在这里测试


说明

数组的隐式输入U

Ë

将数组中的每个元素都传递给一个函数,该函数D将当前元素放在其中。

ÆD

生成一个长度数组,D并用填充D

c

展平。

ö

获取一个随机元素。



1

Perl,31个字节

@a=map{($_)x$_}@ARGV;$a[rand@a]

假定输入为命令行参数。请注意,如果数量很大,它可能会耗尽内存。




1

木炭,12字节

F⪪θ;FIι⊞υι‽υ

在线尝试!链接是详细版本的代码。由于木炭试图变得太聪明,因此我必须对数组使用分号分隔的输入。说明:

  θ             Input variable as string
 ⪪ ;            Split on semicolons
F               Loop i over each string
     Iι         Cast i to integer
    F           Repeat that many times
       ⊞υι      Push i to (originally empty) list
          ‽υ    Random selection from list
                Implicitly print


1

Javascript(ES6),61 54字节

-7个字节,感谢@Justin Mariner

a=>a.find(m=>(n-=m)<0,n=Math.random()*eval(a.join`+`))

示例代码段

f=
a=>a.find(m=>(n-=m)<0,n=Math.random()*eval(a.join`+`))
console.log(f([4,1,5]))


您可以使用eval(a.join`+`)代替来求和reduce
贾斯汀·马里纳

如果您对ES7 +没问题,可以使用:[].find(m=>(n-=m)<0,n=Math.random()*eval(a.join+ ))并致电input::[].find(...)
Downgoat

1

Haskell78 77字节

import System.Random
f l=randomRIO(0,sum l-1)>>=pure.((l>>= \n->n<$[1..n])!!)

在线尝试!用法示例:f [1,99]可能会产生99

说明:

  • f接受一个整数列表,l并以形式返回随机选择的整数IO Int
  • l>>= \n->n<$[1..n]构造一个列表,每个元素n重复n一次。
  • randomRIO(0,sum l-1) 产生一个整数,范围是0到重复元素列表的长度,该长度恰好是所有元素的总和,减去一个负数可以避免超出范围的异常。

奖励: 85字节无积分版

import System.Random
(>>=).randomRIO.(,)0.pred.sum<*>(pure.).(!!).(>>= \n->n<$[1..n])

在线尝试!



1

爪哇8,127个 122 121字节

import java.util.*;a->{List l=new Stack();for(int i:a)for(int j=i;j-->0;Collections.shuffle(l))l.add(i);return l.get(0);}

-1个字节感谢@Nevay

使用与@ErikTheOutgolfer的Jelly答案相似的方法,方法是将该n项目添加时间n到列表中,然后从该列表中随机选择一个。

说明:

在这里尝试。

import java.util.*;        // Required import for List, Stack and Collections
a->{                       // Method with integer-array parameter and integer return-type
  List l=new Stack();      //  Create a List
  for(int i:a)             //  Loop (1) over the input array
    for(int j=i;j-->0;     //   Inner loop (2) from `i` down to 0
        Collections.shuffle(l))
                           //   and shuffle the List randomly after every iteration
      l.add(i);            //    Add `i` that many times to List `l`
                           //   End of inner loop (2) (implicit / single-line body)
                           //  End of loop (1) (implicit / single-line body)
  return l.get(0);         //  And then return the first item of the list
}                          // End of method

1
您可以将#shuffle调用移到for循环中以节省1个字节for(int j=i;j-->0;Collections.shuffle(l))l.add(i);
Nevay

@Nevay谢谢!每次迭代之后对列表进行改组是非常低效的,但是当我们可以摆脱一个额外的讨厌字节时,我们关心效率,警告等问题。; p
Kevin Cruijssen


1

GNU APL 1.2,26 23个字节; 1.7 21 19字节

该方法受Outgolfer的果冻答案Erik启发。依赖于⎕IO0而不是1,这是GNU APL的默认值(的+5字节⎕IO←0)。

-3,-2个字节,感谢@Zacharý

功能形式

∇f R
S[?⍴S←∊0 0⍉R∘.⍴R]∇

匿名lambda形式

{S[?⍴S←∊0 0⍉⍵∘.⍴⍵]}

为了进行说明,我将使用代表传递给函数的参数来表示,但这等效R形式。

⍵∘.⍴⍵使用reshape()运算符计算列表上的外部乘积。有效地,这将创建一个表(如乘法表),但是与其相乘而不是相乘,而是将列中的元素重复与行中的元素相等的次数。对于问题中给出的示例,这是:

4 4 4 4    1 1 1 1    5 5 5 5   
4          1          5         
4 4 4 4 4  1 1 1 1 1  5 5 5 5 5

0 0⍉⍵∘.⍴⍵转置矩阵并仅返回主对角线。这只给我们其中行和列⍵∘.⍴⍵相同的部分,即我们将数字重复了多次,等于其值。例如,这是:

4 4 4 4  1  5 5 5 5 5

将其参数转换为列表。使用transpose()运算符,我们得到了一个包含3个向量的向量。Enlist()将其转换为包含所有元素的单个向量。

S←...将此新向量分配给vector S⍴S给出了清单的长度。?是随机运算符,因此?⍴S为我们提供了一个介于0和列表长度(不包括)之间的随机数(这就是为什么它依赖于⎕IO0;否则它介于1和长度之间(包括一个端点))。S[...]返回给定索引处的元素。


您不需要Q,因为您从不使用它。和IIRC,您可以删除del之前的换行符(小三角
符号

哇,我从不陌生,<IO> <IO>⍉甚至无法获得主要对角线!
扎卡里

@Zacharý对,谢谢。坦白说,直到我尝试此任务之前,我什至都不知道转置的事情。在这里
Arc676

哦,确实存在比GNU更好的免费APL,称为ngn APL。它实际上很酷!(ngn.github.io/apl/web,但它并没有tradfn)
扎卡里

@Zacharý我也有一个:)不幸的是,转置功能不起作用(或者我错过了一些东西)。现在,我对它的工作方式有了更好的了解之后,将再次对其进行测试。
Arc676

1

MATLAB,30个字节

@(a)datasample(repelem(n,n),1)

假定使用MATLAB R2015a或更高版本,并且安装了Statistics&Machine Learning工具箱。

有关repelem用法,请参见以下说明。较短的代码与下面的代码之间的区别在于,S&ML工具箱包含的函数datasample可用于从数组中随机(以均等概率)获取一个或多个元素,从而允许使用匿名函数,从而删除了input/disp电话。

MATLAB,49字节

n=input('');a=repelem(n,n);disp(a(randi(nnz(a))))

此代码假定repelem在引入函数时即使用MATLAB R2015a或更高版本。repelem是具有两个参数的函数,第一个是要复制的数字数组,第二个是应复制对应元素多少次的数组。本质上,该函数通过提供数字和游程长度来执行游程长度解码。

通过为两个输入提供相同的输入,repelem我们得到一个数组,如果有意义的话,该数组包含n倍于元素n。如果您提供,[1 2 3]您将得到[1 2 2 3 3 3]。如果您提供,[1 2 4 2]您将得到[1 2 2 4 4 4 4 2 2]。这样做意味着如果我们选择一个具有均等概率的元素(以均等概率randi(m)给出从1到m的随机整数),则每个元素n的被选概率都高出n倍。在的第一个示例中[1 2 3]1机会为1/6,机会2为2/6,机会3为3/6。


附带说明一下,由于repelem尚不支持Octave,因此无法提供TIO链接。此外,因为倍频不能使用有一个大字处罚input()disp()需要被用来作为一个匿名函数是不可能的。如果支持Octave repelem,则可以使用以下内容:

@(n)a(randi(nnz(a=repelem(n,n))))

那会节省16个字节,但事实并非如此。


真的很感谢解释,谢谢!
伊恩·
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.