Randomly select from an array

19

2

This challenge is rather simple:
You are given an array of positive (not including 0) integers, and have to select a random element from this array.

But here's the twist:
The probability of selecting an element is dependent on the value of the integer, meaning as the integer grows larger, the probability of it to get selected does too!

Example

You are given the array [4, 1, 5].

The probability of selecting 4 is equal to 4 divided by the sum of all elements in the array, in this case 4 / ( 4 + 1 + 5 ) = 4 / 10 =40%.
The probability of selecting 1 is 1 / 10 or 10%.

Input

An array of positive integers.

Output

Return the selected integer if using a method, or directly print it to stdout.

Rules

  • This is so shortest code in bytes in any language wins.
  • Standard loopholes are forbidden.

Ian H.

Posted 2017-09-14T17:08:04.290

Reputation: 2 431

Answers

20

Jelly, 3 bytes

x`X

Try it online!

Look 'ma, no Unicode!

Explanation:

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

Erik the Outgolfer

Posted 2017-09-14T17:08:04.290

Reputation: 38 134

1Can you explain what your code does, please? :) – Ian H. – 2017-09-14T17:39:50.350

1@IanH. It's really a simple algorithm repeat each element itself times then choose randomly. – Erik the Outgolfer – 2017-09-14T17:43:56.430

16

R, 25 bytes

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

Try it online!

Explanation:

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

Takes a sample from s of size 1 without replacement, with weights s; these are rescaled to be probabilities.

To verify the distribution, use this link.

Giuseppe

Posted 2017-09-14T17:08:04.290

Reputation: 21 077

you beat me to it by 9 months! :D – JayCe – 2018-06-09T14:00:49.090

@JayCe heh, my only advantage over you seems to be "going first" as you're quite the golfer! :-) – Giuseppe – 2018-06-09T14:26:04.680

13

Pyth, 4 bytes

OsmR

Try it here.

Saved one byte, thanks to @Jakube, with a rather unusual approach.

Pyth, 5 bytes

Osm*]

Try it here!

How?

#1

OsmR   - Full program.

   R   - Right Map...
  m    - ... Using Map. This essentially creates the list [[4,4,4,4], [1], [5,5,5,5,5]]. 
       - ... Credit goes to Jakube for this!
 s     - Flatten.
O      - Random element of ^. Display implicitly.

#2

Osm*]   - Full program.

  m     - Map over the input.
    ]   - The current element, d, wrapped; [d].
   *    - Repeated d times.
 s      - Flatten.
O       - Random Element. Implicitly print the result.

Mr. Xcoder

Posted 2017-09-14T17:08:04.290

Reputation: 39 774

1I can do it in 4. Should I spoil it, or do you want to find it by yourself? – Jakube – 2017-09-14T19:48:55.637

2@Jakube Wait a tiny bit. Wanna see if I can do it. Is it that obvious? – Mr. Xcoder – 2017-09-14T19:49:37.740

No, not obvious. I think it is even a bit strange. It uses only common things, but I don't think I have seen this combination yet. – Jakube – 2017-09-14T19:55:47.673

1@Jakube Ok, I'll ping when I give up. – Mr. Xcoder – 2017-09-14T19:56:19.530

@Jakube TBH, I have absolutely no idea... Spoil? – Mr. Xcoder – 2017-09-14T19:58:22.167

BTW If you do spoil, you are welcome to edit in, because I'll most likely be sleeping. – Mr. Xcoder – 2017-09-14T20:18:48.777

1OsmL or OsmR – Jakube – 2017-09-14T20:23:09.697

@Jakube >.> - How could I miss that? – Mr. Xcoder – 2017-09-14T20:37:11.050

1@Jakube Ooh that is very clever! Implicit argument d, then maps d over a range...genius! – Erik the Outgolfer – 2017-09-15T10:51:44.163

8

CJam (9 bytes)

q~_]ze~mR

Online demo. This is a full program which takes input in CJam array format on stdin and prints the selected element to stdout.

Dissection

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

Peter Taylor

Posted 2017-09-14T17:08:04.290

Reputation: 41 901

1A so powerful golf for such a simple task. – Erik the Outgolfer – 2017-09-14T18:07:31.503

7

Perl 6, 20 bytes

Saved 1 byte thanks to @Brad Gilbert b2gills.

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

Try it online!

This takes 1 list argument. We zip 2 copies of this list using the xx operator. So with @_ Zxx@_, we get a list in which element x is presented x times. It is then coerced to Bag, which is a collection that stores objects along with how many times they appear in the collection. Finally, we pick a random element from this collection with pick, which takes the counts into the account and does The Right Thing™.

Ramillies

Posted 2017-09-14T17:08:04.290

Reputation: 1 923

This can be shortened to {bag(@_ Z=>@_).pick} – Brad Gilbert b2gills – 2017-09-15T00:21:04.177

@BradGilbertb2gills, sadly that doesn't work. It makes a bag from the pairs (so there would be not "1" once, "2" twice etc., but "1 => 1" once, "2 => 2" also once etc. — not what I want). That's because composers are not coercers, as explained in this Advent Calendar.

– Ramillies – 2017-09-15T01:17:46.110

@BradGilbertb2gills, but thank you anyway, you helped me to golf out some spaces here and in other challenges too! – Ramillies – 2017-09-15T01:22:25.910

I meant {bag(@_ Zxx@_).pick} – Brad Gilbert b2gills – 2017-09-15T16:05:01.727

Aah, I see. Why it didn't occur to me... :--) Thanks. – Ramillies – 2017-09-15T17:10:54.673

6

Python 3.6, 44 bytes

lambda A:choices(A,A)[0]
from random import*

Yay for built-ins. The other A in choices(A, A) is an optional weights argument.

Try it online!

shooqie

Posted 2017-09-14T17:08:04.290

Reputation: 5 032

5

MATL, 8 6 bytes

tY"1Zr

Try it at MATL Online!

Explanation

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

Luis Mendo

Posted 2017-09-14T17:08:04.290

Reputation: 87 464

5

Mathematica, 19 bytes

RandomChoice[#->#]&

J42161217

Posted 2017-09-14T17:08:04.290

Reputation: 15 931

5

Ruby, 32 bytes

->a{a.flat_map{|x|[x]*x}.sample}

Try it online!

daniero

Posted 2017-09-14T17:08:04.290

Reputation: 17 193

4

Python 3, 62 bytes

lambda k:choice(sum([x*[x]for x in k],[]))
from random import*

Try it online!

Mr. Xcoder

Posted 2017-09-14T17:08:04.290

Reputation: 39 774

4

Java (OpenJDK 8), 88 87 86 83 bytes

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

Try it online!

Nevay

Posted 2017-09-14T17:08:04.290

Reputation: 421

Could you add an explanation? I'm trying to understand why the for(r*=Math.random();;) is needed, or if all you need is r*=Math.random(). – Ayb4btu – 2017-09-15T19:52:30.773

@Ayb4btu Without the for(;;) loop this would require a second (never reached) return statement after the for(int i:a)... to satisfy the compiler - which would be 3 bytes longer. – Nevay – 2017-09-15T20:04:49.940

Ah, of course, your for(int i:a) is like a foreach in C#. I had the same problem, but just used a for that continually loops. Your new answer intrigues me, I might try and pilfer some of your ideas. – Ayb4btu – 2017-09-15T20:19:38.180

3

J, 8 7 8 bytes

The 7 byter is invalid; I'll revert this to a previous edit when I get back to my computer in a day or two.

Try it online!

?@+/{#~

:( selecting random elements from an array is costly.

8 bytes

#~{~1?+/

9 bytes

(1?+/){#~

Explanation

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

cole

Posted 2017-09-14T17:08:04.290

Reputation: 3 526

?@+/ is (?@+)/; I'm afraid you'll have to bump that up to 8 again… – FireFly – 2017-09-16T20:48:33.220

@FireFly I should've tested it more, good catch. – cole – 2017-09-16T21:23:34.373

3

JavaScript (ES6), 50 bytes

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

Hopefully it's apparent how this works, but I'll explain it here anyway. It sorts the integers in descending order, then chooses one at random with a beta distrubution (1/2,1).

kamoroso94

Posted 2017-09-14T17:08:04.290

Reputation: 739

I don't think this will have the correct distribution. My tests show that with a=[4,1,5], you'll get about 18% 1, 24% 4, and 58% 5, which suggests you'll get that distribution with any input of length 3.

– Giuseppe – 2017-09-18T15:04:59.223

That seems correct to me. Higher integer, higher probability. – kamoroso94 – 2017-09-18T15:05:39.267

Oh, I see. I misread the question. Excellent solution, +1! – Giuseppe – 2017-09-18T15:07:21.363

2

05AB1E, 9 bytes

εD¸.×}˜.R

Try it online!

Erik the Outgolfer

Posted 2017-09-14T17:08:04.290

Reputation: 38 134

2

PowerShell, 27 bytes

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

Try it online!

Takes input $args[0] as a literal array. Loops through each element |%{...} and each iteration constructs a new array ,$_ of $_ elements -- e.g., for 4 this will create an array @(4,4,4,4). Those array elements are then piped into Get-Random which will pluck out one of the elements with (pseudo) equal probability. Since, e.g., for @(4,1,5) this gives us @(4,4,4,4,1,5,5,5,5,5) this satisfies the probability requirements.

AdmBorkBork

Posted 2017-09-14T17:08:04.290

Reputation: 41 581

2

Japt, 7 bytes

ËÆD
c ö

Test it here


Explanation

Implicit input of array U.

Ë

Map over the array passing each element through a function where D is the current element.

ÆD

Generate an array of length D and fill it with D.

c

Flatten.

ö

Get a random element.

Shaggy

Posted 2017-09-14T17:08:04.290

Reputation: 24 623

2

C# (.NET Core), 93 89 87 76+18 = 94 bytes

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

Try it online!

An extra 18 bytes for using System.Linq;

Acknowledgements

11 bytes saved thanks to Nevay, whose random number implementation was a lot more concise (as well as being an int instead of a double).

Degolfed

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

Explanation

Get a random number, r, between 0 and sum of elements. Then at each iteration subtract the current element from r. If r is less than 0, then return this element. The idea is that there are bigger portions of the random number for the larger numbers in the array.

Ayb4btu

Posted 2017-09-14T17:08:04.290

Reputation: 541

94 bytes: a=>{int i=-1,r=new Random().Next(a.Sum());for(;r>=0;)r-=a[++i];return a[i];} – Nevay – 2017-09-15T23:50:23.453

2

CJam, 5 bytes

lS/mR

Try it online! Note: seperate numbers by a space

lolad

Posted 2017-09-14T17:08:04.290

Reputation: 754

1

Perl, 31 bytes

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

This assumes the input to be command line arguments. Note that it may run out of memory if the numbers are large.

user73921

Posted 2017-09-14T17:08:04.290

Reputation:

1

Perl 5, 31 + 1 (-a) = 32 bytes

@p=map{($_)x$_}@F;say$p[rand@p]

Try it online!

Xcali

Posted 2017-09-14T17:08:04.290

Reputation: 7 671

1

GolfScript, 17 bytes

~{.({.}*}%.,rand=

Try it online!

Erik the Outgolfer

Posted 2017-09-14T17:08:04.290

Reputation: 38 134

1

Charcoal, 12 bytes

F⪪θ;FIι⊞υι‽υ

Try it online! Link is to verbose version of code. Since Charcoal tries to be too clever, I'm having to use semicolon-delimited input for the array. Explanation:

  θ             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

Neil

Posted 2017-09-14T17:08:04.290

Reputation: 95 035

1

Javascript (ES6), 61 54 bytes

-7 bytes thanks to @Justin Mariner

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

Example code snippet

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

Herman L

Posted 2017-09-14T17:08:04.290

Reputation: 3 611

You can sum by using eval(a.join`+`) instead of reduce. – Justin Mariner – 2017-09-14T19:14:18.950

If you're ok with ES7+ you can use: [].find(m=>(n-=m)<0,n=Math.random()*eval(a.join+)) and call with input::[].find(...) – Downgoat – 2017-09-14T23:05:40.543

1

Haskell, 87 bytes

import System.Random
f l|m<-[x<$[1..x]|x<-l]>>=id=randomRIO(0,length m-1)>>=print.(m!!)

Try it online!

jferard

Posted 2017-09-14T17:08:04.290

Reputation: 1 764

1

Haskell, 78 77 bytes

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

Try it online! Usage example: f [1,99] probably yields 99.

Explanation:

  • f takes a list of integers l and returns the randomly selected integer as IO Int.
  • l>>= \n->n<$[1..n] constructs a list with each element n repeated n times.
  • randomRIO(0,sum l-1) yields an integer in the range from 0 to the length of the list of repeated elements, which is exactly the sum of all elements, minus one to avoid a out of bound exception.

Bonus: 85 byte point-free version

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

Try it online!

Laikoni

Posted 2017-09-14T17:08:04.290

Reputation: 23 676

1

Bash, 51 bytes

for n in $@;{ printf $n\\n%.s `seq $n`;}|shuf|sed q

Takes space-separated or newline-separated input in one argument or multiple arguments.

Try it online!

Validate the random frequencies with a more complicated test case.

Justin Mariner

Posted 2017-09-14T17:08:04.290

Reputation: 4 746

1

Java 8, 127 122 121 bytes

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 byte thanks to @Nevay.

Uses a similar approach as @ErikTheOutgolfer's Jelly answer, by adding n times the item n to the list, and then select one randomly from that list.

Explanation:

Try it here.

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

Kevin Cruijssen

Posted 2017-09-14T17:08:04.290

Reputation: 67 575

1You can move the #shuffle call into the for loop to save 1 byte for(int j=i;j-->0;Collections.shuffle(l))l.add(i);. – Nevay – 2017-09-15T14:53:54.120

@Nevay Thanks! Shuffling the List after every iteration is pretty inefficient, but what do we care about efficiency, warnings and such when we can get rid of one additional pesky byte. ;p – Kevin Cruijssen – 2017-09-15T15:03:36.923

1

GNU APL 1.2, 26 23 bytes; 1.7 21 19 bytes

Approach inspired by Erik the Outgolfer's Jelly answer. Relies on ⎕IO being 0 instead of 1, which is the default for GNU APL (sort of +5 bytes for ⎕IO←0).

-3, -2 bytes thanks to @Zacharý

function form

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

Anonymous lambda form

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

For the explanation, I will use to represent the argument passed to the function, but it is equivalent to R in the form.

⍵∘.⍴⍵ computes the outer product on the list using the reshape () operator. Effectively, this creates a table (like a multiplication table) but instead of multiplying, it repeats the element in the column a number of times equal to the element in the row. For the example given in the question, this is:

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⍉⍵∘.⍴⍵ transposes the matrix and returns just the main diagonal. This gives us just the parts where the row and column in ⍵∘.⍴⍵ were the same i.e. we repeated the number a number of times equal to its value. For the example, this is:

4 4 4 4  1  5 5 5 5 5

turns its argument into a list. Using the transpose () operator, we got a vector containing 3 vectors. Enlist () turns it into a single vector containing all the elements.

S←... assigns this new vector to vector S. ⍴S gives us the length of that list. ? is the random operator, so ?⍴S gives us a random number between 0 and the length of the list (exclusive) (this is why it relies on ⎕IO being 0; otherwise it's between 1 and the length, inclusive). S[...] returns the element at the given index.

Arc676

Posted 2017-09-14T17:08:04.290

Reputation: 301

You don't need Q, since you never use it. And IIRC you can remove the newline before the del (little triangle-thing marking the end of the function.) – Zacharý – 2017-09-15T21:05:09.857

Wow, I never new <IO> <IO>⍉ to get the main diagonal was even a thing! – Zacharý – 2017-09-15T21:18:02.877

@Zacharý Right, thanks. Frankly, I didn't even know about the transpose thing until I tried this task either. Found it here

– Arc676 – 2017-09-16T02:39:00.000

Oh, there does exist a much better free APL than GNU, it's called ngn APL. It's actually pretty cool! (https://ngn.github.io/apl/web/, but it doesn't have tradfn)

– Zacharý – 2017-09-16T11:48:45.307

@Zacharý I have that one too :) unfortunately the transpose function doesn't work (or I missed something). I will be testing it again now that I have a better understanding of how it works. – Arc676 – 2017-09-16T11:50:29.200

https://ngn.github.io/apl/web/#code=0%200%u23492%202%u2374%u23734, it works :} – Zacharý – 2017-09-16T11:53:55.970

@Zacharý for some reason I get 404? But thanks for testing it :) – Arc676 – 2017-09-16T12:12:37.827

1

Dyalog APL, 8 bytes

/⍨⌷⍨1?+/

Try it online!

How?

  • /⍨, n copies of n for each n in the argument.
  • ⌷⍨, at the index of
  • 1?, a random value between 1 and
  • +/, the sum of the argument

Zacharý

Posted 2017-09-14T17:08:04.290

Reputation: 5 710

1

MATLAB, 30 bytes

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

This assumes MATLAB R2015a or newer and with the Statistics & Machine Learning toolbox installed.

See the explanation below for how repelem is used. The difference between this shorter one and the one below is that the S&ML toolbox includes the function datasample which can be used to take one or more elements from an array at random (with uniform probability) which allows an anonymous function to be used, stripping away the input/disp calls.

MATLAB, 49 bytes

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

This code assumes that MATLAB R2015a or newer is used as that is when the repelem function was introduced. repelem is a function which takes two parameters, the first is an array of numbers to be replicated, and the second is an array of how many times the corresponding element should be replicated. Essentially the function performs run-length decoding by providing the number and the run-length.

By providing the same input to both inputs of repelem we end up with an array which consists of n times more of element n if that makes sense. If you provided [1 2 3] you would get [1 2 2 3 3 3]. If you provided [1 2 4 2] you would get [1 2 2 4 4 4 4 2 2]. By doing this it means that if we select an element with uniform probability (randi(m) gives a random integer from 1 to m with uniform probability), each element n has an n times higher probability of being selected. In the first example of [1 2 3], 1 would have a 1/6 chance, 2 would have a 2/6 chance and 3 would have a 3/6 chance.


As a side note, because repelem is not available yet for Octave, I can't give a TIO link. Additionally because Octave can't be used there is a big character penalty as input() and disp() need to be used as an anonymous function is not possible. If Octave supported repelem, the following could be used:

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

That would have saved 16 bytes, but it was not to be.

Tom Carpenter

Posted 2017-09-14T17:08:04.290

Reputation: 3 990

Really appreciate the explanation, thanks! – Ian H. – 2017-09-17T10:24:13.497

0

Perl 6, 21 bytes

{flat($_ Zxx$_).pick}

Try it online!

$_ Zxx $_ zips the input list with itself using the xx replication operator, turning (for example) (1, 2, 3) into ((1), (2, 2), (3, 3, 3)). flat flattens that into a list of integers, and finally pick picks one at random.

Sean

Posted 2017-09-14T17:08:04.290

Reputation: 4 136

0

JavaScript ES6, 40 bytes.

(a)=>a[parseInt(Math.random()*a.length)]

Example

((a)=>a[parseInt(Math.random()*a.length)])([4,1,5])

Ideabile

Posted 2017-09-14T17:08:04.290

Reputation: 1

3From the question: The probability of selecting an element is dependent on the value of the integer, meaning as the integer grows larger, the probability of it to get selected does too!. For example, if the array is [4,1,5], you should get a 5 more often than a 1. – H.PWiz – 2017-09-14T22:31:40.360

0

Clojure, 64 bytes

(defn a[i](rand-nth(reduce #(concat %(take %2(repeat %2)))[]i)))

Appends each item item number of times to a new list, then takes a random element.

Finally a clojure answer that's not incredibly much longer than other answers :D

Joshua

Posted 2017-09-14T17:08:04.290

Reputation: 231

0

05AB1E, 21 bytes

O/ždT6m/svy-D0‹s})1kè

Try it online!


Here's a weird implementation for you, uses microseconds on the system's CPU for the randomized seed.

O/                     # [.4,.1,.5] | Push prob distribution.
  ždT6m/               # ?????????? | 0 < x < 100000 divided by 100000.
        s              # [.4,.1,.5] | Swap...
         v             # For each prob in distribution...
          y-           # Remove from random number b/w 0 and 1.
            D0‹        # Dupe each, find if this number made the random number less than 0.
               s})     # Continue loop, swapping current random diff to the front of the stack.
                  1kè  # First instance of the random number going negative is our random return.

Magic Octopus Urn

Posted 2017-09-14T17:08:04.290

Reputation: 19 422

0

Clojure, 46 bytes

(fn[s](rand-nth(flatten(map #(repeat % %)s))))

Try it online!

The usual Clojure pain: simple idea, long-ass (for golfing) function names.

MattPutnam

Posted 2017-09-14T17:08:04.290

Reputation: 521

0

TI-BASIC, 20 bytes

Ans→L₁
SortD(L₁
L₁(1+int(rand²dim(L₁

Input and output are stored in Ans. If you see a box, L₁ is L1. Same algorithm as my JavaScript answer.

kamoroso94

Posted 2017-09-14T17:08:04.290

Reputation: 739