简体   繁体   中英

Ruby: calculating number of unique permutations without using .permutation with duplicates in data set

I require a method for taking in a string of varying length and content, returning the number of permutations without repetition. I have written the following to try to solve this problem

def permutations(n)
  k = n.to_s.chars.uniq.length
  e = n.to_s.length
  m = (1..e).reduce(1, :*)
  p = (1..k).reduce(1, :*)
  l = m / p
  case
    when k == 1 then 1
    when k < e then l
    else m 
  end
end 

The above is returning some results that I've been confused by for a couple of days which I've realised occur where there are more than 1 set of duplicate values for uniq to check.

If I pass through bbbb789 I get 210 which is correct. However if I have a set with two duplicates such as 73839 the expected result is 60 but I reach 5

I realised yesterday where the issues were but I can't find a way to factor in the duplicates

Also my first method for solving this was to use

k = n.to_s.chars.uniq.length
m = n.to_s.chars.length 
return 1 if k <= 1 
n.to_s.chars.permutation(m)to_a.uniq.size

This also worked but takes an age to cycle through all permutations of longer sets

TL;DR:

def factorial(n)
  (1..n).inject(1, :*)
end

str = '73839'

# https://stackoverflow.com/questions/5470725/how-to-group-by-count-in-array-without-using-loop
chars_count = str.split('').inject(Hash.new(0)) { |h, e| h[e] += 1 ; h }

# https://stackoverflow.com/questions/9560335/ruby-hash-to-array-of-values
chars_fact = chars_count.values.inject(1) {|result, element| result*factorial(element)}

p "There are #{factorial(str.length)/chars_fact} permutations without duplicates."

Not very good explanation :

Well, this is mostly a math problem:

Note : When I write n! you should read "factorial n" and it represents the integer 1*2*...*n . You can find a ruby implementation here : https://stackoverflow.com/a/12415362/4480140

If you had na's and mb's then the formula to find the number of permutations without duplicates is n choose m+n which is (m+n)!/(n!*m!).

Then, what we do if we have 'aazzerty' is we say we have a's and b's. So we have 'aabbbbbb', we have 2 choose 8 ways of permuting. One of the possible permutations would be 'bbabbbab'. Then we permute the b's. We know that these b's contain 2 z's and 1 of (e,r,t,y). We will permute everything that is not az or an a. We have 2 choose 6 ways of doing that. We repeat the process ...

In the end the number of permutations is (2 choose 8) (2 choose 6) (1 choose 4) ... (1 choose 2). We can cancel out, in fact we get 8!/(2!*2!*1!*1!*1!*1!).

Basically, we have to count the number of each character, take the factorial of all those numbers, multiply them together. That's the denominator, and the numerator is factorial the length of the string.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM