Hamlet: Better Random Strings

I don't like Lorem Ipsum. Why? Because I'm a student of Latin. Whenever I see the Lorem Ipsum nonsense, my brain spends more time trying to decode it than anything else. I know it's just randomized Cicero, but my brain tries to troll me.

So, I'm just going to use English. Instead of Lorem Ipsum, I use Hamlet. I took a section from Hamlet and threw it into PowerShell. Then, I optimized it down to just a few lines so I can reuse it in samples without much bloat:

By the way, Hamlet has 4752 distinct words with each inflection and declension being counted (e.g. "I", "be", "being", "was", and "am" being five counted as distinct words, though they really aren't).

$genData = 'o my offence is rank it smells to heaven hath the primal eldest curse upont a brothers murder pray can i not though inclination be as sharp will stronger guilt defeats strong intent and like man double business bound stand in pause where shall first begin both neglect what if this cursed hand were thicker than itself with blood there rain enough sweet heavens wash white snow whereto serves mercy but confront visage of whats prayer two-fold force forestalled ere we come fall or pardond being down then ill look up fault past form serve turn forgive me foul that cannot since am still possessd those effects for which did crown mine own ambition queen may one retain corrupted currents world offences gilded shove by justice oft tis seen wicked prize buys out law so above no shuffling action lies his true nature ourselves compelld even teeth forehead our faults give evidence rests try repentance can yet when repent wretched state bosom black death limed soul struggling free art more engaged help angels make assay bow stubborn knees heart strings steel soft sinews newborn babe all well'.split(' ')

$gen = {
    param ($count)
    1..$count | foreach {$r = @()} { $r += $genData[(random) % $genData.count] } {[String]::Join(' ', $r)}
}

&$gen 10

Don't care about Powershell? Chill. Python and JavaScript versions are at the end.

"like man teeth shall turn since repentance turn bound corrupted"

It's worth noting that if you run &$gen 100, you might be a candidate for some sort of poetry award:

so help intent rests a foul strong visage angels our double stubborn effects no angels did buys not law death i action since man oft is soul wash possessd which were we snow confront cursed shove forgive so up murder there offence repentance faults i but our forestalled black forgive man out serve look can defeats engaged down one mercy assay assay ere knees were retain gilded struggling a then prayer there law engaged prayer evidence of hand faults snow white a itself we business defeats serve double stand struggling smells defeats but repent heart both since defeats out shall

I should mention that if you wanted a concise way to capitalize the first letter, that's definitely doable:

$gen = {
    param ($count)
    1..$count | foreach {$a = @()} { $a += $genData[(random) % $genData.count] } {[String]::Join(' ', $a)}
}

All this works so far, but it's not very efficient.

Let's use our super-duper object-oriented, results-focused, enterprise-class benchmark software:

$then = [DateTime]::Now
&$gen 400
$now = [DateTime]::Now
Write-Host ($now - $then).Seconds

Our fancy benchmark enterprise-class reporting software shows 818ms on my machine.

What can we do better? Raw .NET:

if (!([System.Management.Automation.PSTypeName]'_netfxharmonics.hamlet.Generator').Type) {
    Add-Type -Language CSharp -TypeDefinition '
        namespace _Netfxharmonics.Hamlet {
            public static class Generator {
                private static readonly string[] Words = "o my offence is rank it smells to heaven hath the primal eldest curse upont a brothers murder pray can i not though inclination be as sharp will stronger guilt defeats strong intent and like man double business bound stand in pause where shall first begin both neglect what if this cursed hand were thicker than itself with blood there rain enough sweet heavens wash white snow whereto serves mercy but confront visage of whats prayer two-fold force forestalled ere we come fall or pardond being down then ill look up fault past form serve turn forgive me foul that cannot since am still possessd those effects for which did crown mine own ambition queen may one retain corrupted currents world offences gilded shove by justice oft tis seen wicked prize buys out law so above no shuffling action lies his true nature ourselves compelld even teeth forehead our faults give evidence rests try repentance can yet when repent wretched state bosom black death limed soul struggling free art more engaged help angels make assay bow stubborn knees heart strings steel soft sinews newborn babe all well".Split('' '');
                private static readonly int Length = Words.Length;
                private static readonly System.Random Rand = new System.Random();

                public static string Run(int count, bool subsequent = false) {
                    return Words[Rand.Next(1, Length)] + (count == 0 ? "" : " " + Run(count - 1, true));
                }
            }
        }

    '
}

You call this with:

[_netfxharmonics.ps.Generator]::Run(400)

Rerunning our fancy enterprise-class benchmark again...

$then = [DateTime]::Now
[_netfxharmonics.ps.Generator]::Run(400)
$now = [DateTime]::Now
Write-Host ($now - $then).Milliseconds

This time I get 8ms. Yeah. Eight.

Man, given this speed, we can increase our dictionary (see below to download hamlet_distinct.t):

if (!([System.Management.Automation.PSTypeName]'_netfxharmonics.hamlet.Generator').Type) {
    Add-Type -Language CSharp -TypeDefinition '
        namespace _Netfxharmonics.Hamlet {
            public static class Generator {
                private static readonly string[] Words = System.IO.File.ReadAllText(@"E:\Drive\Documents\Content\NetFX\NetFXContent\2015\03\hamlet\hamlet_distinct.t").Split('' '');
                private static readonly int Length = Words.Length;
                private static readonly System.Random Rand = new System.Random();

                public static string Run(int count, bool subsequent = false) {
                    return Words[Rand.Next(1, Length)] + (count == 0 ? "" : " " + Run(count - 1, true));
                }
            }
        }

    '
}

This needs to work in more than simply PowerShell. I do a lot of work in both Python and JavaScript, so...

Python

#!/usr/bin/env python

from random import randint

genData = 'o my offence is rank it smells to heaven hath the primal eldest curse upont a brothers murder pray can i not though inclination be as sharp will stronger guilt defeats strong intent and like man double business bound stand in pause where shall first begin both neglect what if this cursed hand were thicker than itself with blood there rain enough sweet heavens wash white snow whereto serves mercy but confront visage of whats prayer two-fold force forestalled ere we come fall or pardond being down then ill look up fault past form serve turn forgive me foul that cannot since am still possessd those effects for which did crown mine own ambition queen may one retain corrupted currents world offences gilded shove by justice oft tis seen wicked prize buys out law so above no shuffling action lies his true nature ourselves compelld even teeth forehead our faults give evidence rests try repentance can yet when repent wretched state bosom black death limed soul struggling free art more engaged help angels make assay bow stubborn knees heart strings steel soft sinews newborn babe all well'.split(' ')

def gen(count):
    return genData[randint(1, len(genData) - 1)] + ('' if count == 0 else ' ' + gen(count - 1))

print gen(300)

JavaScript

var genData = 'o my offence is rank it smells to heaven hath the primal eldest curse upont a brothers murder pray can i not though inclination be as sharp will stronger guilt defeats strong intent and like man double business bound stand in pause where shall first begin both neglect what if this cursed hand were thicker than itself with blood there rain enough sweet heavens wash white snow whereto serves mercy but confront visage of whats prayer two-fold force forestalled ere we come fall or pardond being down then ill look up fault past form serve turn forgive me foul that cannot since am still possessd those effects for which did crown mine own ambition queen may one retain corrupted currents world offences gilded shove by justice oft tis seen wicked prize buys out law so above no shuffling action lies his true nature ourselves compelld even teeth forehead our faults give evidence rests try repentance can yet when repent wretched state bosom black death limed soul struggling free art more engaged help angels make assay bow stubborn knees heart strings steel soft sinews newborn babe all well'.split(' ');

function gen(count) {
    return genData[parseInt(Math.random() * genData.length)] + (count == 0 ? '' : ' ' + gen(count - 1));
}

console.log(gen(300))

Want the full hamlet_distinct.t? OK, fine....