How big does a random generator have to be?

As I’ve mentioned, I’m working on a D&D app that’s a content generator of all kinds: it spits out encounters, NPCs, treasure, and cool locations.

One makes a random generator by coming up with a bunch of lists, each as large as possible, and crashing them into each other. The bigger each list is, the more depth and reusability your generator has.

How do you know when you can stop adding to a random table? That’s important to me because I don’t just want to make a random generator that looks cool the first time you use it; I want it to provide useful stuff one, two, ten years from now.

the birthday paradox

Maybe you’ve heard this classic math puzzle: in a room of 25 people, what are the odds of at least 2 of them sharing a birthday?

Unintuitively, even though the odds of any 2 people sharing a birthday is only 1/365, the odds of any shared birthday among 25 people is more than 50%. In a room of 60 people, you’re almost guaranteed (99%) to find at least one shared birthday. That’s weird, right? It’s true because we’re looking for ANY shared birthday. The odds of anyone in the room sharing YOUR birthday are still fairly small.

Here’s the full explanation.

You can use this same math to determine how many rolls on a chart you expect before you get a duplicate. A room with 25 people looking for shared birthdays is analogous to rolling 25 times on a chart with 365 entries. On a d20 chart, you can expect to roll a duplicate after only 6 rolls. On a d100 chart, you need only 13 rolls before you’re more likely than not to roll a duplicate. It’s amazing how quickly those duplicates start arriving!

Of course, a chart isn’t totally useless just because you rolled one duplicate. But at some point the diminishing returns may start seriously reducing the value of the chart.

Brute-forcing the birthday paradox calculations further, we find the following:
-If you roll 25 times on a d100 chart, you’re likely to have seen about 22 unique table entries. 3 of your rolls (12%) will have resulted in you seeing a result you’ve already seen.
-If you roll 50 times on a d100 table, you’re likely to have rolled around 40 unique entries. 10 of your rolls (20%) will have generated recycled results.
-After 75 rolls, you will have seen 50 of the table results. 25 of your rolls (33%) will be duplicates.
-After 100 rolls, you will have seen 65 different results and 35 duplicates.

Thus, in total, after 100 rolls on a d100 table, you will have seen something new 65% of the time, and had the experience of seeing recycled material 35% of the time. To adopt the language of rottentomatoes.com, your experience has been 65% fresh.

These percentages hold true for any number of items on a table, just so the proportions of entries to rolls is the same; so after your first 50 rolls on a d100 table, or your first 500 rolls on a d1000 table, you can expect your experience to be 80% fresh.

We have a judgement call to make here, but to me, unless all the entries are highly reusable, a “65% fresh” table feels stale; therefore, if I plan to use a table 100 times over its lifetime, I probably need way more than 100 entries. Let’s choose “80% fresh” as an arbitrary cutoff. At 80% fresh, you see something new on 4 out of every 5 uses of a table. To hit this target, you need to follow this rule of thumb: if you plan to use a random table N times, you need more than (N times 2) table entries.

This seems like a useful thing to know as a D&D designer, and it gets us most of the way towards knowing how big a dice chart or random generator needs to be to do its job. There’s still one more piece of the equation.

reusable vs one-use

When creating a generator, I try to distinguish between reusable and one-use list items.

Reusable items can come up many times and they don’t make a nuisance of themselves. They’re not obtrusive. They also tend to be rather vague and nonspecific. For example, as part of 5e Inspiration, I’m writing a list of random desert locations. “Dunes of golden sand” is a reusable item: over the course of a long desert journey, you’d expect it to come up frequently. “Sandstorm” is a reusable item too. Although it should come up with less frequency than sand dunes, you don’t begrudge multiple sandstorms over the course of a few desert adventures.

One-use items are specific enough that they feel unique. They tend to be more interesting than reusable items. Unfortunately, they don’t wear well. One-use items are the mechanism by which a random generator ages: once you have seen a few of them twice, you’re hitting diminishing returns for the whole random generator. One-use examples from my desert encounters list:

  • a sinkhole filled with salt; inside is a salt-caked sailing ship filled with dessicated sailor corpses
  • strange, rusty, ancient towers filled with still-operational chugging machinery that doesn’t appear to do anything. With a DC 14 Intelligence check, you can figure out how to activate the towers, which might be water pumps, oil pumps, or arcane devices that cause all damage spells cast within a mile to do maximum damage
  • rainbow sand dunes: if a beast is encountered here, it may also be rainbow colored
  • As a player or DM, I’d roll my eyes if I saw any of these twice.

    A generator with no one-use items is evergreen, but rather dull. A generator with all one-use items, according to the birthday paradox math we did above, needs to be big enough that most of its entries will never be rolled.

    Based on your mix of one-use items in your generator, and how many times you want it to be used, you can determine how much work you need to put into coming up with new entries.

    Most of the random tables I write are a mix of fairly generic, multi-use results and rare, interesting, one-use results. To figure out how that impacts a table’s “freshness” calculation, ignore the number of die rolls that are likely to return a generic, multi-use result. For instance: If you’re rolling 100 times on a d100 chart, but half of the chart’s results are generic and reusable, treat it as if you are rolling only 50 times on the table. Thus, for 100 rolls, your half-generic d100 table is 80% fresh.

    the freshness calculator

    Here’s a “freshness” calculator you can use to figure out how big your random table needs to be to meet your desired level of interestingness. This calculator uses brute force, simulating results 1000 or so times.

    what about multi-table random results?

    Not all random generators are a single die-roll chart. Many are in the format “roll once on table A, once on table B, etc”. For instance, a tavern name generator could be a single d100 chart where you get a complete name like “The Golden Goose” if you roll a 36, but it’s more likely to be 2 d20 charts, where you roll the “The Golden” on table 1 and “Goose” on table 2. How do you evaluate the freshness of these grouped tables?

    I think that for multiple tables, you evaluate each of the tables separately, and then you use the worst result. People are really good at spotting patterns. Once the party has been to The Golden Goose, The Red Goose, and The Unnamed Goose taverns, you just can’t have any more goose tavern names. It doesn’t matter that you never rolled a duplicate on table 1. The repetition on table 2 makes the whole game world feel more creaky and procedural.

    how do 5e tables fare?

    Now, let’s use this procedure to evaluate a few of the random tables that come in the 5e Dungeon Masters Guide.

    5e has been around since 2014. Assuming you’ve run a weekly game for the past 5 years, how “fresh” are the following tables?

    The official 5e magic item properties tables. There are 4 tables, ranging from d8 to d20, with 60 properties total. The instructions are to “roll on as many as you like.” Let’s assume you’ve rolled on only one table per magic item, and only for major magic items: say, one die roll every two sessions. By now, you’ve rolled about 120 times, and there’s only 60 entries, so you’ve hit a lot of dupes (43% fresh). Chances are you’ve given up on these charts already. I bet you rolled for magic item properties 10 or 15 times, hit a few duplicates, used the charts as inspiration lists a few more times, and then stoped altogether. That’s roughly what I’ve done, anyway.

    How many random magic item characteristics would you need to provide, say, an “80% fresh” experience for 125 rolls over the course of 5 years? Plugging in numbers into the freshness calculator, it seems that 300 characteristics would just about do it. That’s a far cry from the 60 that are provided.

    The NPC traits charts. There’s a d20 chart for NPC appearance. How many of the items are reusable and how many are one-shot? I’d say it’s maybe 50/50, with “flamboyant clothes” and “bald” being reusable, and “nervous eye twitch” and “missing fingers” being unique. If I ran into 2 NPCs who were missing fingers, I’d suspect they were the same doppelgänger.

    If you’ve used this chart to make just 1 NPC per week for the last 5 years, you have 12 folks running around your campaign world with missing fingers. Clearly this table is not big enough. To provide specific and fresh results for 250 NPCS over 5 years, you probably want a d500 table at least.

    the DMG vs the Inspiration app

    OK, it’s not completely fair to judge the DMG charts this way. They’re clearly meant to provide inspiration – to teach you how to customize your magic items and NPCs. After rolling on the charts for a few sessions, you’re supposed to be able to do your own homework before each session – spread your creative wings and fly!

    Well, that’s bully for the DMG. I, on the other hand, am not here to help anyone learn to fly. I’m here to do your homework for you! The Inspiration app currently has about 500 NPC characteristics, many of them evergreen, and about 2000 magic item variants. It should be able to provide you dungeon mastering freshness for the next 10 years at least.

    Next week, let’s look at some magic item variants from the app.

    Sign up for the Inspiration beta test!

    Read more about the Inspiration app

    8 Responses to “How big does a random generator have to be?”

    1. Daniel Stevens says:

      Since you’re making an app, couldn’t you just take advantage of the format and guarantee 100% freshness by cutting duplicates? This would require you to keep some sort of local save file, granted, and might not play well with generic reusables like “sandstorm” or “bald”.

      It would also lead to everyone eventually seeing everything on the table, for good and for ill.

    2. Matthew Neagley says:

      Are you sure about the math here? The “n rolls on 2n table is always ~80% fresh” seems counter intuitive to me. Especially since the final generalized result from your source is (number of rolls to reach) = sqrt(-2*ln(1-(target prob 1+ matches))) * root(total options)

      Since sqrt(-2*ln(1-(target prob 1+ matches))) is constant for a given target prob, that means that as number of items on a table goes up, number of picks to hit your target prob of having 1+ matches goes up not linearly but as a function of sqrt(number of items), which means it takes a smaller and smaller ratio of picks to total entries to hit your first match. And a smaller ratio of picks to entries to get a first match SHOULD intuitively lead to a smaller proportion of unique entries which should imply a lower freshness ratio the higher n is in your “n rolls on a 2n table”
      I suspect to maintain the same freshness you need not a 2n table but an n^2 table.

    3. paul paul says:

      @Daniel: Good point. In fact I’m testing a feature similar to that, which I may go into more detail about later: I shuffle each list like a deck of cards instead of rolling on it with dice. The caveat: with my implementation, it gets re-shuffled when the number of the items in the list changes – so, whenever I update the app, lists may be re-shuffled if I’ve added content.

      “Common” items appear multiple times in a list, so it’s possible to hit “sandstorm” or “dunes” several times in a row, the same way you could have 2 kings in a row in a deck of cards. Rare items only appear once, so they’ll never appear consecutively, except if the rare item is the last card in the deck, the deck is reshuffled, and then the rare item happens to be the first card in the deck in the new order.

      However, despite shuffling, I decided to keep the aggressive “rolling on a chart” calculations for determining optimal table size. The shuffle method means I’m providing a 100% fresh experience (barring reshuffles forced by an update) until the list is exhausted, followed by a 0% fresh experience for the rest of time. I want to make sure that the fresh content will last for years.

    4. Yomar says:

      “A generator with all one-use items, according to the birthday paradox math we did above, needs to be big enough that most of its entries will never be rolled.”

      Is that something you’d want? It’s less labour intensive to just mark one-use entries that have been rolled, and replace them after the session. Otherwise you end up many many entries that are never used.

    5. Paul says:

      @Matthew: My math may well be wrong! However, I’m trying to do something slightly different from the birthday problem. I don’t really care when you hit the first duplicate, just when you’ve hit your (N * .2)th duplicate. And that seems to be linear, at least according to my simulations.

      I did some more research and I discovered that what I’m doing is actually the generalized version of the coupon problem. Frankly, the math is beyond me.

      I decided to test by taking my hypothesis – that you always expect to have around 20% duplicate rolls on n rolls on a table with n*2 entries – and write a new, super-simple simulation. I hypothesize that if I run this simulation with values of n of 100, 1000, 10,000, and 100,000, I should find, each time, that 78-80% of my rolls were “fresh”. The code is here. https://jsfiddle.net/c9zb7mh6/

      In fact, unless there’s a js bug, it seems to work as expected: for any value of n that’s large enough to eliminate wild random swings, we get a value of 78% to 80% fresh.

    6. Matthew Neagley says:

      @Paul: It’s hard to argue with empirical simulations, esp when you’re modeling exactly the behavior you’re looking for (like you are) and not an approximation.

    7. allan grohe says:

      Paul—

      I’m curious to hear your assessment on two different types of tables I use with some regularity: 1) nested tables, and 2) what I can ranging bell-curve tables (I have no idea if a real term exists for these, and would be glad to learn a more-proper name if you know one!).

      #1 – Nested Tables: they’re different from your table 1 + table 2 variation, since the nestings are still driven by initial probability results. I have two examples from my Knockspell articles BITD on my blog. The first is a dungeon dressing table for dungeon doors @ https://grodog.blogspot.com/2017/05/dungeon-strangitude-variations-on.html that uses a simple two levels of nesting, while the second example is the effects caused by the destruction of a gate @ https://grodog.blogspot.com/2017/05/the-theory-and-use-of-gates-in-campaigndungeons-part-2.html and it features two and three levels of nesting in its results, along with some variables within each result based on die rolls (which I’m assuming get discounted from your calculations since they’re still the same variable results when rolled).

      #2 – Ranging Bell-Curve Tables: I’ve used these when designing my WM tables in my Black Reservoir Castle Greyhawk level. I use multiple dice for the WM to generate a curved results set vs. a single/flat die roll, and I built two different WM charts which varied by 7 different depth levels as well as surface/sub-surface, which influenced what could be encountered where and how often. Basically, the closer to the shore you were, the more frequent the encounter checks were, but the deeper you went, the more likely that really nasty monsters were wandering around:

      Black Reservoir: The Shallows

      – 0-15′ = 1 in 6, check every 3 turns; use 3d6-2 for encounter
      – 16-35′ = 1 in 6, check every 2 turns; use 3d6 for encounter
      – 36-60 = 1 in 8, check every 2 turns; use 3d6+2 for encounter
      – 61-100′ = 1 in 8, check every 2 turns; use 3d6+4 for encounter

      Black Reservoir: The Depths

      – 101-150′ = 1 in 8, check every 3 turns; use 4d6 for encounter
      – 151-250′ = 1 in 8, check every 3 turns; use 4d6+2 for encounter
      – 251-450′ = 1 in 8, check every 3 turns; use 4d6+4 for encounter

      The Shallows table has results numbered 1-22 (with some minor variations within a result like in the D1-3 WM tables), while The Depths table has results numbered 4-28.

      The full tables as examples are posted to the Knights & Knaves Alehouse @ https://knights-n-knaves.com/phpbb3/viewtopic.php?p=160465#p160465

      Allan.

    8. paul paul says:

      @allan,

      Both of these are useful and ranging bell-curve tables are brilliant.

      Nested tables, as you call them, drive most of my app. For instance, Encounter #20 of the Level 5 Desert encounter table looks like this:

      20. half-gold dragon holy knight
      roll d8 for companions:
      1-4 no companions
      5: riding giant flying lizard*
      6: with 1-6+6 dragonborn warriors*
      7: riding giant flying lizard*, with 1-6+4 dragonborn warriors*
      8: with blue dragon wyrmling

      I have nested tables like this for each of something like, maybe, 7000 encounters, which is insane, but there’ a big payoff in encounter variation.

      Nested tables offer you less bang for your buck than table1+table2 variations but create better results. By less bang for your buck, I mean that you may spend time designing a table that may come up infrequently or never at all. On the other hand, they can have an unmatched level of specificity and bespoke-ness, and all the options are curated and non-absurd. If your design time isn’t valuable, nested tables are great.

      As a hobbyist, I consider my D&D time not valuable, since I don’t need my time investment to pay for itself, and I’m having fun designing no matter how many times my work gets seen.

      2) I’ve used tables like your “ranging bell curve” table but only with one die – like, roll d6 and add the dungeon level to determine which of 15 encounter tables to roll on. I think this is a great way to flatten things that often use a matrix, such as the 1e “monster determination and level of monster matrix”.

      However, using the bell curve is even better in this case, since it gives that nice distribution of a tightly clustered group in the middle, and the remote possibility of wildly over- or under-leveled encounters. A 1 in 216 chance of a dragon on level 1! Let’s go for it!

    Leave a Reply