54
\$\begingroup\$

About the Series

This is a guest entry for the Random Golf of the Day series.

First off, you may treat this like any other code golf challenge, and answer it without worrying about the series at all. However, there is a leaderboard across all challenges. You can find the leaderboard along with some more information about the series in the first post.

Input

No input is taken.

Output

A single letter of the alphabet (case irrelevant), with an optional trailing newline. Each letter must have non-zero probability of being chosen, and all 26 probabilities must be distinct. To remove all ambiguity: Distinct means that there must not be two probabilities that are equal to each other.

Scoring

This is code golf. Shortest code in bytes wins.

A valid entry is a full program or function that has zero probability of not terminating.

Alphabet

To avoid confusion, the particular alphabet to be used is the Latin alphabet:

Either

ABCDEFGHIJKLMNOPQRSTUVWXYZ

or

abcdefghijklmnopqrstuvwxyz

You may choose to output upper case or lower case. Alternatively, you may choose to output different cases on different runs if that helps. The probability for a given letter is the probability of that letter appearing in either case (upper or lower).

Explanation

As it won't be at all obvious from the output, please include a clear explanation of how you achieved the 26 distinct probabilities.

Leaderboard

(from here)

var QUESTION_ID=89621,OVERRIDE_USER=20283;function answersUrl(e){return"https://api.stackexchange.com/2.2/questions/"+QUESTION_ID+"/answers?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function commentUrl(e,s){return"https://api.stackexchange.com/2.2/answers/"+s.join(";")+"/comments?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+COMMENT_FILTER}function getAnswers(){jQuery.ajax({url:answersUrl(answer_page++),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){answers.push.apply(answers,e.items),answers_hash=[],answer_ids=[],e.items.forEach(function(e){e.comments=[];var s=+e.share_link.match(/\d+/);answer_ids.push(s),answers_hash[s]=e}),e.has_more||(more_answers=!1),comment_page=1,getComments()}})}function getComments(){jQuery.ajax({url:commentUrl(comment_page++,answer_ids),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){e.items.forEach(function(e){e.owner.user_id===OVERRIDE_USER&&answers_hash[e.post_id].comments.push(e)}),e.has_more?getComments():more_answers?getAnswers():process()}})}function getAuthorName(e){return e.owner.display_name}function process(){var e=[];answers.forEach(function(s){var r=s.body;s.comments.forEach(function(e){OVERRIDE_REG.test(e.body)&&(r="<h1>"+e.body.replace(OVERRIDE_REG,"")+"</h1>")});var a=r.match(SCORE_REG);a&&e.push({user:getAuthorName(s),size:+a[2],language:a[1],link:s.share_link})}),e.sort(function(e,s){var r=e.size,a=s.size;return r-a});var s={},r=1,a=null,n=1;e.forEach(function(e){e.size!=a&&(n=r),a=e.size,++r;var t=jQuery("#answer-template").html();t=t.replace("{{PLACE}}",n+".").replace("{{NAME}}",e.user).replace("{{LANGUAGE}}",e.language).replace("{{SIZE}}",e.size).replace("{{LINK}}",e.link),t=jQuery(t),jQuery("#answers").append(t);var o=e.language;/<a/.test(o)&&(o=jQuery(o).text()),s[o]=s[o]||{lang:e.language,user:e.user,size:e.size,link:e.link}});var t=[];for(var o in s)s.hasOwnProperty(o)&&t.push(s[o]);t.sort(function(e,s){return e.lang>s.lang?1:e.lang<s.lang?-1:0});for(var c=0;c<t.length;++c){var i=jQuery("#language-template").html(),o=t[c];i=i.replace("{{LANGUAGE}}",o.lang).replace("{{NAME}}",o.user).replace("{{SIZE}}",o.size).replace("{{LINK}}",o.link),i=jQuery(i),jQuery("#languages").append(i)}}var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe",COMMENT_FILTER="!)Q2B_A2kjfAiU78X(md6BoYk",answers=[],answers_hash,answer_ids,answer_page=1,more_answers=!0,comment_page;getAnswers();var SCORE_REG=/<h\d>\s*([^\n,]*[^\s,]),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/,OVERRIDE_REG=/^Override\s*header:\s*/i;
body{text-align:left!important}#answer-list,#language-list{padding:10px;width:290px;float:left}table thead{font-weight:700}table td{padding:5px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr></thead> <tbody id="answers"> </tbody> </table> </div><div id="language-list"> <h2>Winners by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr></thead> <tbody id="languages"> </tbody> </table> </div><table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table>

The first post of the series also generates an overall leaderboard.

To make sure that your answers show up, please start every answer with a headline, using the following Markdown template:

## Language Name, N bytes

where N is the size of your submission. If you improve your score, you can keep old scores in the headline, by striking them through. For instance:

## Ruby, <s>104</s> <s>101</s> 96 bytes

(The language is not currently shown, but the snippet does require and parse it, and I may add a by-language leaderboard in the future.)

\$\endgroup\$
5
  • \$\begingroup\$ how would you measure 26 distinct probabilities? by running program 26 times? \$\endgroup\$
    – YOU
    Aug 12, 2016 at 12:47
  • 1
    \$\begingroup\$ @YOU have a look through the solutions - there are a few different approaches with excellent explanations \$\endgroup\$ Aug 12, 2016 at 12:49
  • \$\begingroup\$ If it is a function does it need to print or can it just return the character value? \$\endgroup\$ Aug 13, 2016 at 6:12
  • \$\begingroup\$ @Geoff According to our defaults for input and output, either printing to STDOUT or returning a character are fine. \$\endgroup\$ Aug 13, 2016 at 10:48
  • \$\begingroup\$ @Geoff note that it must be a character, not just a number value representing it. For example, A rather than 65. \$\endgroup\$ Aug 13, 2016 at 10:50

62 Answers 62

2
\$\begingroup\$

CJam, 10 bytes

CJam approach #3...

26mr)mr'A+

Try it online!

This creates a uniformly random number x between 1 and 26 and then uses that to create a uniformly random number between 0 and x-1 which is added to A. This biases results towards smaller characters.

\$\endgroup\$
2
\$\begingroup\$

Mathematica, 34 bytes

RandomChoice[Range@26->Alphabet[]]

RandomChoice[wlist -> elist] gives a random choice of elist weighted by wlist.

\$\endgroup\$
1
  • \$\begingroup\$ Damn, beat me to it! \$\endgroup\$ Aug 14, 2016 at 9:21
2
\$\begingroup\$

Ruby, 42 bytes

$><<(?A..?Z).flat_map{|c|[c]*c.ord}.sample

Quite straightforward: Generate 65 A's; 66 B's; ... 90 Z's, and randomly pick one of the letters generated.

\$\endgroup\$
2
\$\begingroup\$

LaTeX, 122 115 bytes

-7 bytes by replacing pgfmathparse{Hex(...)} with pgfmathHex{...}.

Or, if I'm allowed to skip the document class definition & setup, and just count the package import and functional code: 65 bytes.

pgfmathHex parses the expression and its (hexadecimal) result is then fed into \char which turns the code point into a unicode character. The expression itself is identical to many other answers here.

\documentclass{book}\usepackage{tikz}\begin{document}\pgfmathHex{65+sqrt(rnd)*26}\char"\pgfmathresult\char"\pgfmathresult\end{document}

Ungolfed (with cherry-picked for loop for page-filling output):

\documentclass{book}
\usepackage{tikz}
\begin{document}
\noindent
\foreach \n in {0,...,1513}
{
\pgfmathHex{65+sqrt(rnd)*26}\char"\pgfmathresult
}
\end{document} 

Output (w/ free page number :) ):

enter image description here

\$\endgroup\$
2
\$\begingroup\$

C#, 68 66 bytes

Thanks to Leaky Nun for fixing the chance distribution.

var r=new Random();char f(char c='A')=>c<'Z'&r.Next(3)>0?f(++c):c;

Surprisingly short for a C# answer.

It defines a function f that returns a random character between the given one and Z. It has a 1/3 chance of returning the current character, otherwise it recurses on the next. If the current character is Z, it always returns. Default parameter is A, so calling it without any argument satisfies the challenge.

The resulting chances follow 2^(n-1) / 3^n:

A: 1/3
B: 2/9
C: 4/27
D: 8/81
...
Y: 16777216/847288609443
Z: 67108864/2541865828329
\$\endgroup\$
3
  • 1
    \$\begingroup\$ Y and Z have the same probability. \$\endgroup\$
    – Leaky Nun
    Aug 12, 2016 at 14:35
  • 3
    \$\begingroup\$ You can use 3 instead of 2 to fix this problem. \$\endgroup\$
    – Leaky Nun
    Aug 12, 2016 at 14:35
  • \$\begingroup\$ Fixed the chances, thanks @LeakyNun \$\endgroup\$
    – Scepheo
    Aug 15, 2016 at 8:45
2
\$\begingroup\$

x86_64 machine language for Linux, 15 19 17 16 bytes

L1:
48 0f c7 f0             rdrand %rax
f3 48 0f b8 c0          popcnt %rax,%rax
3c 1a                   cmp    $0x1a,%al
7d f3                   jge    L1
04 41                   add    $0x41,%al
c3                      retq

This requires support for the POPCNT and RDRAND instructions.

A uniform distributed random number is generated, the number of 1's in that number is counted, if that number is less than 26, a letter is returned. One will need to let the code run a long time before one sees a letter A.

To test, try something like

#include<stdio.h>
#define TEST "\x48\xf\xc7\xf0\xf3\x48\xf\xb8\xc0\x3c\x1a\x7d\xf3\4\x41\xc3"
int main(){
  int hist[26]={0};
  for(int i=0;i<10000000;i++){
    hist[ ((int(*)())TEST)() - 'A' ]++;
  }
  for(int i=0;i<26;i++){
    printf("%c %d\n", 'A'+i, hist[i] );
  }
}

Sample output

A 0
B 0
C 0
D 0
E 0
F 0
G 0
H 0
I 0
J 0
K 0
L 8
M 32
N 137
O 511
P 1639
Q 5188
R 14475
S 37539
T 91670
U 205638
V 431381
W 842259
X 1536776
Y 2626524
Z 4206223

The analytical expression for the probability of each letter can be derived from the binomial distribution. The letter A is assigned index k=0, B is assigned k=1 and so on.

        /  \
       | 64 |
       | k  |
        \  /
p(k)=------------
      25
      --- /  \
      \  | 64 |
      /  | i  |
      --- \  /
      i=0

 p(A)~1.0483e-18
 p(B)~6.7093e-17
 p(C)~2.1134e-15
 p(D)~4.3678e-14
 p(E)~6.6608e-13
 p(F)~7.9930e-12
 p(G)~7.8598e-11
 p(H)~6.5124e-10
 p(I)~4.6401e-09
 p(J)~2.8872e-08
 p(K)~1.5879e-07
 p(L)~7.7953e-07
 p(M)~3.4429e-06
 p(N)~1.3772e-05
 p(O)~5.0169e-05
 p(P)~1.6723e-04
 p(Q)~5.1214e-04
 p(R)~1.4460e-03
 p(S)~3.7758e-03
 p(T)~9.1413e-03
 p(U)~2.0568e-02
 p(V)~4.3095e-02
 p(W)~8.4231e-02
 p(X)~1.5381e-01
 p(Y)~2.6276e-01
 p(Z)~4.2042e-01
\$\endgroup\$
1
2
\$\begingroup\$

Matlab, 24 18 bytes

char(floor(26*rand^2+65))

it looks like using floor(x) isn't necessary as char also takes non-integer inputs

char(25*rand^2+65)

not using ['' x] instead of char(x) so I won't get warning: implicit conversion from numeric to char for "purer" output.

rand yields a uniformly distributed random number in the interval (0,1) but rand^2 isn't uniformy distributed anymore, the probability density function follows 1/x, see here.

with (b-a)*rand+a one can shift the interval of the distribution from (0,1) to (a,b), this also works with rand^2.

because i use floor i need to stretch the interval to (65,91) so i don't lose "Z"

It might no be the shortest answer but i like the approach with using a uniform random distribution to get a non-uniform random distribution.

Below is the count for each generated numbers after 20000 iterations (using my first version of my answer).

distribution after 20000 iterations

\$\endgroup\$
5
  • \$\begingroup\$ As long as it gives correct output, I don't see a reason to avoid warnings. Go ahead and save the byte... \$\endgroup\$ Aug 16, 2016 at 22:59
  • \$\begingroup\$ But the warning gets also printed as output, does that still count as it produces more output that the desired character? \$\endgroup\$ Aug 17, 2016 at 4:57
  • \$\begingroup\$ Ah I see. If it's printed in conjunction with the output then that's a problem. I'd suggest checking if there's a command line switch that can suppress the warning at the cost of a byte but to save just one byte it's not worth it... \$\endgroup\$ Aug 17, 2016 at 9:09
  • \$\begingroup\$ Consensus on meta seems to be that warnings to STDERR are fine but warnings to STDOUT are not . I'm not sure which Matlab uses, but mentioning in case it helps. \$\endgroup\$ Aug 17, 2016 at 9:34
  • \$\begingroup\$ I'm not sure too, but thanks for the hint. Although running the code in Matlab which had warnings turned off with warning('off','all') beforehand suppresses the warning - but i have no clue if this complies with the rules? \$\endgroup\$ Aug 17, 2016 at 14:04
2
\$\begingroup\$

MATL, 12 bytes

1Y2toY"tnYr)

Try it online!

Explanation

1Y2          % Pushes the alphabet A...Z
   to        % Make a numeric copy [65 ... 90]
     Y"      % Run length decoding: 65 A's, 66 B's, etc. Let's call this [S]
        nYr  % Random number between 1 and length of [S], let's call this [i]
       t   ) % Index into a copy of [S] at the [i]'th position. Implicit display.

The selection from [S] is according to a uniform distribution, but since each letter in the alphabet occurs a distinct number of times in [S], the resulting distribution is also distinct for each letter in the alphabet.

\$\endgroup\$
2
\$\begingroup\$

SmileBASIC, 26 22 bytes

I think it's unfair that most of these are biased towards earlier letters.

Here's one that prints Z the most:

?CHR$(90-RND(RND(27)))
\$\endgroup\$
2
\$\begingroup\$

Phooey, 26 bytes

~?675@(&>@+1@<--@@^)>+63$c

Try it online!

The demo contains a simple infinite loop which resets the tape (this program expects a clean tape and doesn't clean up lul)

Similar to a lot of other answers, this generates a number from \$0-675\$ and uses roughly \$\lfloor \sqrt{n}\rfloor\$ to select the letter with varying probability.

Unfortunately, AFAICT, there is no way of doing a square root automatically since the pow operator uses integers. Maybe there is a fixed point method, but idk yet.

However, the naïve subtraction method works and is probably shorter than any other solution. It's only going to be running a few iterations, so the inefficiency isn't that bad.

int64_t isqrt(int64_t n)
{
    int64_t d = 0;
    while (n >= 0) {
        n -= d;
        d += 1;
        n -= d;
    }
    return d - 1;
}

My implementation actually has an off-by-one problem with my loop condition check. I use \$x^x\$ (@^) instead of \$2^x\$ (@&2^) which is the "proper" (at least, in Phooey) way to check for negatives, and when \$x = -1\$, it will report a false negative (or is it a false positive? 🤨), as \${-1}^{-1} = {-1}, {-1} \ne 0\$.

HOWEVER, that doesn't affect the output's randomness. It just means that Z will appear once instead of A and we need \$d - 2\$ instead of \$d - 1\$.

With all values from \$0-675\$ tested, this is the frequency:

A 3   B 5
C 7   D 9
E 11  F 13
G 15  H 17
I 19  J 21
K 23  L 25
M 27  N 29
O 31  P 33
Q 35  R 37
S 39  T 41
U 43  V 45
W 47  X 49
Y 51  Z 1

How it works:

~?675@(&>@+1@<--@@^)>+63$c
~?675                       # Generate a random integer between 0 and 675 inclusive
     @                      # Push the initial n to the stack
                            # Begin our naïve semi-sqrt loop
      (            )        # while n >= 0
       &                    #    Pop n from the stack
        >                   #    Move left to d
         @                  #    Push d
          +1                #    Increment d
            @               #    Push d + 1
             <              #    Move back to n
              --            #    Subtract d and d + 1, popping them
                            #    Use a modified method I found in my Brexit post to check for < 0
                @           #    Save a copy of n
                 @^         #    Calculate n^n using double<->int64 conversion
                            #    The current cell will contain zero if n < -1, or nonzero otherwise.
                            #    Use that as our loop condition.
                    >       # Move to d
                     +63    # d += 'A' - 2
                        $c  # print as char
\$\endgroup\$
2
+50
\$\begingroup\$

Vyxal, 5 bytes

ka¦∑℅

Try it Online!

same idea as the Pyth answer.

\$\endgroup\$
4
  • \$\begingroup\$ new codepage \$\endgroup\$
    – lyxal
    Apr 9, 2021 at 2:19
  • \$\begingroup\$ @Lyxal looks much better \$\endgroup\$
    – Razetime
    Apr 9, 2021 at 2:21
  • \$\begingroup\$ That's exactly why I decided to change the code page \$\endgroup\$
    – lyxal
    Apr 9, 2021 at 3:01
  • \$\begingroup\$ Try it Online! for 4 bytes \$\endgroup\$
    – lyxal
    Jun 20, 2023 at 12:47
2
\$\begingroup\$

Zsh, 26 bytes

eval shuf -en1 {{a..z}..z}

Try it online! Includes a basic frequency analysis.

Explanation:

  • {a..z} - construct the string a b ... z
  • {{a..z}..z} - construct the string {a..z} {b..z} ... {z..z}
  • evaluate that string - executes shuf -en1 a b ... z b c ... z c d ... z ... y z z
  • shuf -en1 - picks one of those letters at random. In the pool, z is repeated 26 times, y 25 times, etc., which creates the distinct probabilities.
\$\endgroup\$
2
\$\begingroup\$

05AB1E, 4 bytes

AƶJΩ

Try it online! Beats all other answers.

AƶJΩ  # full program
   Ω  # random character from...
  J   # joined...
A     # "abcdefghijklmnopqrstuvwxyz"...
 ƶ    # with each character repeated 1-indexed index of character in string times
      # implicit output

AƶJ generates the string 'abbcccddddeeeeeffffffggggggghhhhhhhhiiiiiiiiijjjjjjjjjjkkkkkkkkkkkllllllllllllmmmmmmmmmmmmmnnnnnnnnnnnnnnoooooooooooooooppppppppppppppppqqqqqqqqqqqqqqqqqrrrrrrrrrrrrrrrrrrsssssssssssssssssssttttttttttttttttttttuuuuuuuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvvvvvvvvwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzzzzzzzzz', so the chances are as follows:

a: 0.285%
b: 0.57%
c: 0.855%
d: 1.14%
e: 1.425%
f: 1.709%
g: 1.994%
h: 2.279%
i: 2.564%
j: 2.849%
k: 3.134%
l: 3.419%
m: 3.704%
n: 3.989%
o: 4.274%
p: 4.558%
q: 4.843%
r: 5.128%
s: 5.413%
t: 5.698%
u: 5.983%
v: 6.268%
w: 6.553%
x: 6.838%
y: 7.123%
z: 7.407%

Alternative, 4 bytes

AηJΩ

Try it online!

AηJΩ  # full program
   Ω  # random character from...
  J   # joined...
 η    # prefixes of...
A     # "abcdefghijklmnopqrstuvwxyz"
      # implicit output

AηJ generates the string 'aababcabcdabcdeabcdefabcdefgabcdefghabcdefghiabcdefghijabcdefghijkabcdefghijklabcdefghijklmabcdefghijklmnabcdefghijklmnoabcdefghijklmnopabcdefghijklmnopqabcdefghijklmnopqrabcdefghijklmnopqrsabcdefghijklmnopqrstabcdefghijklmnopqrstuabcdefghijklmnopqrstuvabcdefghijklmnopqrstuvwabcdefghijklmnopqrstuvwxabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyz', so the chances are as follows:

a: 7.407%
b: 7.123%
c: 6.838%
d: 6.553%
e: 6.268%
f: 5.983%
g: 5.698%
h: 5.413%
i: 5.128%
j: 4.843%
k: 4.558%
l: 4.274%
m: 3.989%
n: 3.704%
o: 3.419%
p: 3.134%
q: 2.849%
r: 2.564%
s: 2.279%
t: 1.994%
u: 1.709%
v: 1.425%
w: 1.14%
x: 0.855%
y: 0.57%
z: 0.285%
\$\endgroup\$
1
\$\begingroup\$

Clojure, 61 bytes

#(char(- 90(count(take-while(fn[a](<(rand)0.5))(range 26)))))

Takes each successive element from the range of 26 elements with probability 1/2. So the chance that one element is taken is 1/2, 2 elements - 1/4 and so on. After that subtract from 90 number of elements taken and turn it into char.

p(Z) = 1/2, p(Y) = 1/4, p(X) = 1/8 ... p(A) = 1/2^(26)

See it online: https://ideone.com/Cg15FY

\$\endgroup\$
2
  • \$\begingroup\$ This appears to give the same probability for A and B. Imagine it for an alphabet of only 3 letters ABC: p(C) = 1/2, p(B) = 1/4, p(A) = 1 - 1/2 - 1/4 = 1/4. \$\endgroup\$ Aug 13, 2016 at 11:43
  • \$\begingroup\$ Unless A is also chosen with a probability of 1/2 when it is reached, with the possibility of wrapping back round to Z? If it always terminates when it reaches A then p(A) = p(B) = 1/2^(25). \$\endgroup\$ Aug 13, 2016 at 11:48
1
\$\begingroup\$

Julia, 33 bytes

f()=['A':'Z'][ceil(26*√rand())]

Applies the square root to RNG.

Graph showing distribution: enter image description here Generated with:

using Gadfly
plot(x=ceil(26*√rand(10000)),Geom.histogram)
\$\endgroup\$
1
\$\begingroup\$

ForceLang, 64 bytes

Noncompeting, uses language features (the string.char method) that postdate the question.

set s math.sqrt 676.mult random.rand()
io.write string.char 65+s
\$\endgroup\$
1
  • \$\begingroup\$ Override header: Valid but noncompeting (ignore this - it's just to let the leaderboard snippet know) \$\endgroup\$ Aug 13, 2016 at 0:16
1
\$\begingroup\$

Actually, 7 bytes

ú#;╚♀MJ

Try it online!

This answer uses the same approach as Dennis's Jelly answer.

Explanation:

ú#;╚♀MJ
ú#       lowercase English alphabet, as a list (this is to dodge a bug with the shuffle command)
  ;╚     duplicate, shuffle
    ♀M   pairwise maximum
      J  random element
\$\endgroup\$
1
\$\begingroup\$

perl, 21 bytes

say+(A..Z)[rand$$%27]

Needs -M5.010 or -E to run :

perl -E 'say+(A..Z)[rand$$%27]'


How it works : (A..Z) is an array. $$ is process ID, which introduces some randomness. $$%27 is a number between 0 and 26 (inclusive, and roughly evenly spread). Calling rand on that produces a number between 0 and 26 (inclusive at 0, exclusive at 26), but biased towards smaller numbers. We then use that number for an array lookup, which we print.

Opportunities for improvement :

$$ is random, but not cleanly random. We know that the chance of getting particular numbers differs, but not by much, and not in ways that are easy to predict cleanly. Therefore, the possibility of getting different each letter is probably not identical, but it's very close. If (and only if) we accept that probability{ $$%26 == x } is different for all x, then we could replace "rand$$%27" with "$$%26".

Alternately, we could replace "rand$$%27" with (for example) "26*sin$$", and save one character. This gives a much less regular spread than simply "$$%26":

 perl -E 'for(1..32768){say+(A..Z)[26*sin$_]}' | sort | uniq -c
    832 A
   3269 B
   1648 C
   1360 D
   1183 E
   1133 F
   1043 G
   1071 H
    959 I
    978 J
    985 K
    939 L
    926 M
    950 N
    924 O
    939 P
    985 Q
    977 R
    959 S
   1072 T
   1043 U
   1135 V
   1182 W
   1360 X
   1647 Y
   3269 Z

But if you check, you'll see that it's not perfect. There are three pairs of numbers that have the same probability as each other: K&Q, I&S, L&P.

(If you use actual PIDs, then depending on what processes are actually running, and depending on the highest allowable pid on your machine, you might get more clashes, or less.)

"15*sin$$" works better, only 2 clashes - but that is still 2 clashes. (Any number between 13 and 26 will work, in the sense of producing all 26 letters, because sin(x) spans -1 to +1 and perl treats negative number array look-ups as starting from the array end).

There might be some way of getting all 26 numbers, with different probabilities, using less (or no more) than the 8 characters that "rand$$%27" needs. But if so, it isn't coming to me.

\$\endgroup\$
0
1
\$\begingroup\$

Clojure, 62 bytes

(loop[i 65](if(or(> i 89)(neg?(rand)))(char i)(recur(inc i))))

This is somewhat stupid shortcut. The probability of exact zero double - the only (rand) to not break on condition (pos? x) - is around 1/(2^62) [source].

So chance for letter char(65+N) is around (1/(2^62))^N, slightly higher for Z (because it is last).

With (rand-int 2) - 6 more bytes - it becomes testable.

\$\endgroup\$
1
\$\begingroup\$

Perl 6, 30 bytes

{('a'..'z'Zxx 1..*).flat.pick}

Explanation:

# bare block lambda
{
  (

    'a' .. 'z' # the alphabet
    Z[xx]      # zipped 「Z」 using the list repeat operator 「xx」
    1 .. *     # with 1 to 26

    # ((a)(b b)(c c c)(d d d d)(e e e e e)...

  )
  .flat # flatten it from a list of lists into a single list
  .pick # pick an element from the list
}
\$\endgroup\$
1
\$\begingroup\$

Pip, 8 bytes

(zr*r*h)

z is a built-in for the lowercase alphabet, h for 100. r is a special variable: each time it is evaluated, it generates a random number with uniform distribution, 0 <= r < 1. Thus, r*r*h squares the uniform distribution and scales it a hundredfold (saving a character over using 26). The result is used to index into z, auto-truncated and with index wrapping.

Try it here (frequency chart of 100,000 runs, takes about 30 seconds).

\$\endgroup\$
2
  • 1
    \$\begingroup\$ It wasn't immediately obvious to me that folding the unevenly distributed range of 100 values into a range of 26 values would maintain the property of having all probabilities distinct (especially as the frequency chart has some letters appearing similar numbers of times). I checked and can confirm that the probabilities (assuming a perfect uniform distribution as a starting point) are all distinct: \$\endgroup\$ Aug 17, 2016 at 14:04
  • \$\begingroup\$ 0.122257379845413, 0.063400059354823, 0.0534956778948075, 0.0482511384937917, 0.04481748680079, 0.0423168133306285, 0.0403735674712969, 0.0387960044737913, 0.0374744304593056, 0.0363408541731965, 0.0353505332076944, 0.0344726212707875, 0.0336850437718526, 0.0329715122041705, 0.0323196958133572, 0.0317200547970168, 0.0311650693170861, 0.0306487146656851, 0.0301660946708953, 0.0297131797962578, 0.0292866162765532, 0.028883584540095, 0.023514130401554, 0.0231759623356073, 0.0228547631421794, 0.0225490114913639 \$\endgroup\$ Aug 17, 2016 at 14:04
1
\$\begingroup\$

Lua, 100 99 bytes

s="A"math.randomseed(os.time())for i=66,90 do
s=.6<math.random()and string.char(i)or s end
print(s)

Not really competitive, but still an interesting algorithm I think.

\$\endgroup\$
2
  • \$\begingroup\$ Due to there being a probability of 0.5 each time through the loop, it looks like the end of the loop leaves the same probability for Z as for Y (since there can be no halving of the probability when only Z is left). You could get around this by using a figure other than 0.5, which would make the solution valid without adding any bytes. \$\endgroup\$ Aug 17, 2016 at 21:57
  • 1
    \$\begingroup\$ @trichoplax I see what you mean. Changed it, thanks! \$\endgroup\$ Aug 18, 2016 at 23:09
1
\$\begingroup\$

IA-32 machine code, 12 bytes

Hexdump:

0F C7 F0 F6 E4 88 E0 D4 1A 04 61 C3   

Source code:

rdrand eax
mul ah
mov al, ah
aam 26
add al, 'a'
ret

Or, as a C expression (assuming r is a uniformly generated random number of type uint32_t):

(r & 0xff) * ((r >> 8) & 0xff) / 0x100 % 26 + 'a'

Running this expression on values in the range 0...65535 gives the probabilities of the letters:

z 1968
y 2012
x 2068
w 2076
v 2134
u 2140
t 2194
s 2209
r 2233
q 2292
p 2335
o 2358
n 2410
m 2437
l 2475
k 2545
j 2571
i 2654
h 2696
g 2738
f 2818
e 2922
d 2955
c 3098
b 3241
a 3957
\$\endgroup\$
1
\$\begingroup\$

Dyalog APL, 14 bytes

See this meta post for information about the code page.

(?351)⊃⎕A/⍨⍳26

Gives instant results. Works by selecting an evenly distributed random character among "ABBCCCDDDD..."

(?351) RandInt(1,351)
picks among
⎕A "ABC...Z"
/⍨ element-by-element replicated
⍳26 {1, 2, 3, ..., 26} times

TryAPL online!

\$\endgroup\$
2
  • \$\begingroup\$ @MartinEnder. Ouch, that may affect a few submissions. \$\endgroup\$
    – Adám
    May 14, 2017 at 19:51
  • \$\begingroup\$ Well I think some of the leaderboard implementations are a bit smarter than the one I used here. \$\endgroup\$ May 14, 2017 at 19:53
1
\$\begingroup\$

MathGolf, 6 bytes

▄mÅî*w

Try it online.

Explanation:

▄       # Push the lowercase alphabet
 m      # Map over each letter
  Å     # using the following two characters as inner code-block:
   î    #  Push the 1-based map-index
    *   #  Repeat the current character that many times
     w  # After the map: pop and push a random character from this string
        # (after which the entire stack joined together is output implicitly as result)

Same probability as most other answers:

  • a: \$\frac{1}{351}\$
  • b: \$\frac{2}{351}\$
  • ...
  • y: \$\frac{25}{351}\$
  • z: \$\frac{26}{351}\$
\$\endgroup\$
4
  • \$\begingroup\$ I haven't done the exact math on this, but since indexing repeats for indices that are higher than the length of a string/array (e.g. "abcd"9§ gives "b"), you could do something like ▄v²§ (link). Basically, get a random 32 bit integer, square it to skew the probabilities, and index the alphabet using the skewed random integer. \$\endgroup\$
    – maxb
    May 12, 2021 at 7:55
  • \$\begingroup\$ @maxb Unless I'm doing something wrong myself, I'm pretty sure some of the letters will never be output. If we square and then modulo-26, range [0,25] will result in [0,1,4,9,16,25,10,23,12,3,22,17,14,13,14,17,22,3,12,23,10,25,16,9,4,1], but so will range [26,51], [52,77], etc. It's a repeating pattern apparently. Those integers will the only possible outputs, so some letters (i.e. 2=c) will never be output, and the letters that do output have in most cases overlapping probabilities. \$\endgroup\$ May 12, 2021 at 8:28
  • \$\begingroup\$ You're right, I just tried it and noticed that the residues weren't distributed over all 26 possible values. I tried some different things (like the cube/factorial/fibonacci operators), but I haven't had any luck yet. Trying to analyze vv* now (one byte longer, but should avoid some of the issues above) \$\endgroup\$
    – maxb
    May 12, 2021 at 8:35
  • \$\begingroup\$ I found something that works... ▄v±√i§, saving exactly 0 characters. \$\endgroup\$
    – maxb
    May 12, 2021 at 10:33
1
\$\begingroup\$

Javascript, 93 91 bytes

Yes there is a shorter one, but that does it differently. Also I spent half an hour on this so I'm not giving up.

q=>'abcdefghijklmnopqrstuvwxyz'.split``.map((i,k)=>i.repeat(k)).join``[Math.random()*351|0]

Explained:

q=> // define function, meaningless arg saves a byte
'abcdefghijklmnopqrstuvwxyz'.split`` // get each alphabet character in array
.map((i,k)=>i.repeat(k)) //replace each with it repeated index times
.join``[Math.random()*351|0] //turn to string and get random character. 

Probability:

a - 1/351
b - 2/351
c - 3/351

Etcetera.

Note: according to this comment, I don't have to use a function declaration if it isn't recursive.

\$\endgroup\$
0
\$\begingroup\$

Python 3, 58 bytes

from random import*;print(chr(randint(65,randint(65,91))))

it is like that perl answer.

\$\endgroup\$
3
  • \$\begingroup\$ 57 bytes: r=__import__('random').randint;print(chr(r(65,r(65,91)))) \$\endgroup\$
    – FlipTack
    Dec 22, 2016 at 19:28
  • \$\begingroup\$ post yourself? @Flp.Tkc \$\endgroup\$ Dec 22, 2016 at 22:15
  • \$\begingroup\$ I just golfed the import statement, you did the main algorithm, so post it yourself :) \$\endgroup\$
    – FlipTack
    Dec 22, 2016 at 22:52
0
\$\begingroup\$

k4, 15 bytes

*1?a|-26?a:.Q.A

A port of @Dennis's Jelly answer.

\$\endgroup\$
0
\$\begingroup\$

JavaScript ES6, 42 Bytes, modified from user81655's solution:

_=>(0|10+26*Math.random()**2).toString(36)
\$\endgroup\$
0
\$\begingroup\$

Javascript, 49 bytes

f=()=>String.fromCharCode(65+Math.random()**2*26)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.