statistics – How to count duplicates in a mixed pool using AnyDice?

For exact duplicates, the program given in Carcer’s answer to the question you cite can be easily modified to take multiple dice pools:

function: dupes in A:s B:s C:s {
  DICE: (sort {A, B, C})
  DUPES: 0
  loop X over {2..#DICE} {
    if (X-1)@DICE = X@DICE { DUPES: DUPES + 1 }
  }
  result: DUPES
}

output (dupes in 1d12 2d10 1d8)

Note that, like Carcer’s original code, this function will return 3 for (12, 12, 3, 3, 3, 1), not 5, because it counts a group of $n$ identical dice as $n-1$ duplicates! If you do want to count all dice that match at least one other die, here’s a version that will do that:

function: dupes in A:s B:s C:s {
  DICE: (sort {A, B, C})
  DUPES: 0
  loop X over {1..#DICE} {
    PREV_MATCH: X > 1 & (X-1)@DICE = X@DICE
    NEXT_MATCH: X < #DICE & (X+1)@DICE = X@DICE
    if PREV_MATCH | NEXT_MATCH { DUPES: DUPES + 1 }
  }
  result: DUPES
}

output (dupes in 1d12 2d10 1d8)

Note that this version will never yield a result of “one duplicate” — there are always at least two, or none at all!

Alas, because these programs need to iterate through all possible results of all the rolls, they can be rather slow if there are many dice with many sides in the pool. For example, my test outputs above use 1d12, 2d10 and 1d8, because trying to run them for your example of 3d12, 2d10 and 1d8 times out. :/

For bigger dice pools, my Python dice roller from an earlier answer can do the trick instead. Here’s a solution using Carcer’s counting method:

from collections import defaultdict
summary = defaultdict(float)

for d12, p12 in dice_roll(12, count=3):
  for d10, p10 in dice_roll(10, count=2):
    for d8, p8 in dice_roll(8, count=1):
      prob = p12 * p10 * p8
      roll = sorted(d12 + d10 + d8)
      dupes = sum(roll(i) == roll(i+1) for i in range(len(roll) - 1))
      summary(dupes) += prob

for dupes, prob in sorted(summary.items()):
  print("%d duplicates: %.2f%%" % (dupes, 100 * prob))

and here’s one using yours:

from collections import defaultdict
summary = defaultdict(float)

for d12, p12 in dice_roll(12, count=3):
  for d10, p10 in dice_roll(10, count=2):
    for d8, p8 in dice_roll(8, count=1):
      prob = p12 * p10 * p8
      roll = sorted(d12 + d10 + d8)
      dupes = sum(
        (i > 0 and roll(i-1) == roll(i))
        or (i < len(roll)-1 and roll(i+1) == roll(i))
        for i in range(len(roll))
      )
      summary(dupes) += prob

for dupes, prob in sorted(summary.items()):
  print("%d duplicates: %.2f%%" % (dupes, 100 * prob))

(These are also brute force solutions, but Python is faster than AnyDice. And, if you run it on your own computer, it has no time limit.)


All of these programs can be fairly easily modified to also count “near misses”. Since you’ve now split that part into a separate question, I’ve answered it there.