With one die of 6 sides we will have six different possible results:```  1, 2, 3, 4, 5, 6``` .

With 2 dice of six sides, we will have 36 different possible results:
``` 
(1,1),(1,2),(2,1),(1,3),(3,1),(1,4),(4,1),(1,5),
(5,1), (1,6),(6,1),(2,2),(2,3),(3,2),(2,4),(4,2),
(2,5),(5,2)(2,6),(6,2),(3,3),(3,4),(4,3),(3,5),(5,3),
(3,6),(6,3),(4,4),(4,5),(5,4),(4,6),(6,4),(5,5),
(5,6),(6,5),(6,6)
``` 
So, with 2 dice of 6 sides we get 36 different events.
``` 
([6,6] ---> 36)
``` 
But with 2 different dice we can get for this case, the same number of events.
One die of ```4 sides```  and another of ```9 sides```  will produce the exact amount of events.
``` 
([4,9] ---> 36)
``` 
We say that the dice set ```[4,9]``` is equivalent to ```[6,6]``` because both produce the same number of events.

Also we may have an amount of three dice producing the same amount of events. It will be for:
``` 
[4,3,3] ---> 36
``` 
(One die of 4 sides and two dice of 3 sides each)

Perhaps you may think that the following set is equivalent: ```[6,3,2]``` but unfortunately dice have a **minimum of three sides** (well, really a 
tetrahedron with one empty side)

The task for this kata is to get the amount of equivalent dice sets, having **2 dice at least**,for a given set.

For example, for the previous case: [6,6] we will have 3 equivalent sets that are: ``` [4, 3, 3], [12, 3], [9, 4]``` .

You may assume that dice are available from 3 and above for any value up to an icosahedral die (20 sides).
``` 
[5,6,4] ---> 5 (they are [10, 4, 3], [8, 5, 3], [20, 6], [15, 8], [12, 10])
``` 
For the cases we cannot get any equivalent set the result will be `0`.
For example for the set `[3,3]` we will not have equivalent dice.

Range of inputs for Random Tests:
``` 
3 <= sides <= 15
2 <= dices <= 7
``` 
See examples in the corresponding box.

Enjoy it!!

Source: codewars | Difficulty: EASY
Tags: Number theory

Fundamentals

Complete search

Starter code: def eq_dice(set_):
	

Solutions:
import numpy as np

def products(n, min_divisor, max_divisor):
	if n == 1:
		yield []
	for divisor in range(min_divisor, max_divisor + 1):
		if n % divisor == 0:
			for product in products(n // divisor, divisor, max_divisor):
				yield (product + [divisor])

def eq_dice(set):
	product = np.prod(set)
	lista = list(products(product, 3, min(product - 1, 20)))
	return len(lista) - 1 if len(set) > 1 else len(lista)


from functools import reduce

def getDivisors(n):
	(lst, p) = ([], 2)
	while n > 1:
		while not n % p:
			lst.append(p)
			n //= p
		p += 1 + (p != 2)
	return lst

def eq_dice(diceSet):
	events = reduce(int.__mul__, diceSet)
	lstDivs = getDivisors(events)
	combos = set(genCombDices(tuple(lstDivs), set()))
	return len(combos - {tuple(sorted(diceSet))})

def genCombDices(tup, seens):
	if tup[0] != 2:
		yield tup
	if len(tup) > 2:
		for (i, a) in enumerate(tup):
			for (j, b) in enumerate(tup[i + 1:], i + 1):
				m = a * b
				t = tuple(sorted(tup[:i] + tup[i + 1:j] + tup[j + 1:] + (m,)))
				if m > 20 or t in seens:
					continue
				seens.add(t)
				yield from genCombDices(t, seens)


from itertools import combinations_with_replacement
from pprint import pprint

def multiples(f):
	m = []
	for i in range(2, 8):
		m.append(combinations_with_replacement(f, i))
	return m

def eq_dice(set_):
	mul = 1
	for e in set_:
		mul *= e
	facts = [x for x in range(3, 21) if not mul % x]
	count = 0
	for r in multiples(facts):
		for m in r:
			mu = 1
			for e in m:
				mu *= e
			if mu == mul and set(m) != set(set_):
				print(m)
				count += 1
	print(count)
	return count


from functools import reduce

def eq_dice(set_):
	n = reduce(int.__mul__, [s for s in set_ if s not in (11, 13)])
	stack = [[n, [s for s in set_ if s in (11, 13)]]]
	result = set()
	while stack:
		(n, status) = stack.pop()
		if 3 <= n <= 20 and len(status) > 0:
			result.add(tuple(sorted(status + [n])))
		for i in range(3, min(20, int(n // 3)) + 1):
			if n % i == 0 and n // i >= 3:
				stack.append([n // i, sorted(status + [i])])
	result.discard(tuple(sorted(set_)))
	return len(result)


from math import floor, sqrt
from numpy import prod

def f(n, l=3):
	if n < l:
		return
	for p in range(l, min(floor(sqrt(n)) + 1, 21)):
		if not n % p:
			for l in f(n // p, p):
				yield ([p] + l)
	if n <= 20:
		yield [n]

def eq_dice(l):
	return sum((1 for l in f(prod(l)) if len(l) > 1)) - (len(l) > 1)


from functools import reduce
from math import floor, sqrt

def f(n, l=3):
	if n < l:
		return
	for p in range(l, min(floor(sqrt(n)) + 1, 21)):
		if not n % p:
			for l in f(n // p, p):
				yield ([p] + l)
	if n <= 20:
		yield [n]

def eq_dice(l):
	return sum((1 for l in f(reduce(lambda p, n: p * n, l, 1)) if len(l) > 1)) - (len(l) > 1)


def product(dice):
	return dice[int()] * product(dice[1:]) if len(dice) > 1 else dice[int()]

def eq_dice(dice):
	return len(recursive(product(dice), [], [])) - 1 if len(dice) > 1 else len(recursive(product(dice), [], []))

def recursive(num, x, y, dice=3):
	if 3 > num // 3:
		return y
	while dice <= num // dice and dice < 21:
		if num % dice == int():
			if sorted(x + [dice, num // dice]) not in y and num // dice < 21:
				y.append(sorted(x + [dice, num // dice]))
			y = recursive(num // dice, x + [dice], y)
		dice += 1
	return y


from functools import reduce

def get_factors(m):
	(li, j) = ([], 2)
	while j * j <= m:
		if m % j:
			j += 1
			continue
		m //= j
		li.append(j)
	return li + ([m] if m > 1 else [])

def divide(arr, cache):
	store = []
	for i in range(len(arr)):
		for j in range(i + 1, len(arr)):
			m = arr[:i] + arr[i + 1:j] + [arr[i] * arr[j]] + arr[j + 1:]
			tp = tuple(sorted(m))
			if m != arr and len(m) > 1 and (tp not in cache):
				cache.add(tp)
				store.extend([tp] + divide(m, cache))
	return store

def eq_dice(arr):
	(m, arr) = (reduce(lambda x, y: x * y, arr), tuple(sorted(arr)))
	return sum((1 for i in set(divide(get_factors(m), set())) if i != arr and all((2 < k < 21 for k in i))))


from itertools import combinations_with_replacement
from numpy import prod

def eq_dice(set_):
	lst = sorted(set_)
	(eq, dice, count) = (0, [], prod(lst))
	for sides in range(3, 21):
		if count % sides == 0:
			dice.append(sides)
	for num_dice in range(2, 8):
		for c in combinations_with_replacement(dice, num_dice):
			if prod(c) == count and sorted(c) != lst:
				eq += 1
	return eq