Math - Permutation, Solve Tianji Horse Racing and Password Cracking Problems (Notes)

Tianji Horse Racing Story

Tianji was a famous general of Qi, often raced horses with King of Qi, but always lost, very unhappy. Sun Bin wanted to help Tianji. He divided these horses into upper, middle, lower three grades. He let Tianji use his lower grade horse to fight King’s upper grade horse, use upper grade horse to fight King’s middle grade horse, use middle grade horse to fight King’s lower grade horse. After three matches, Tianji only lost first match, won last two matches, finally won entire match with King.

Permutation Concept

From n different elements, take m (1≤m≤n) different elements, arrange them in a sequence according to certain order, this process is called Permutation.

When m=n this special case appears, this is All Permutation

Tianji Horse Racing Permutation Diagram

Figure 1

Final Permutation Count

Permutation total calculation 3x2x1=6

  • For n elements’ all permutations, all possible permutation count is nx(n-1)x(n-2)x…x2x1, which is n!

  • For n elements taking m (0<m≤n) elements’ non-repeating permutation count is nx(n-1)x(n-2)x…x(n - m + 1), which is n!/(n-m)!

Use Code to Calculate Various Situations of Tianji Horse Racing

// King's horses and speeds
let q_horses_time = {
  q1: 1,
  q2: 2,
  q3: 3
}

// Tianji's horses and speeds
let t_horses_time = {
  t1: 1.5,
  t2: 2.5,
  t3: 3.5
}

// Their horse names
let q_horses = ["q1", "q2", "q3"]

let t_horses = ["t1", "t2", "t3"]


/**
  * @Description:  Use function recursion (nesting) calls to find all possible horse battle orders
  * @param horses-How many horses currently not yet battled, result-Save currently battled horses and order
  * @return void
  */

function permutate(horses, result) {
  // All horses have battled, judge which side wins, output result
  if (horses.length == 0) {
    // console.log(result);
    compare(result, q_horses);
    return;
  }

  for (let i = 0; i < horses.length; i++) {
    // From remaining unbattled horses, choose one, add to result
    let new_result = [].concat(result)
    new_result.push(horses[i])

    // Remove selected horse from unbattled list
    let rest_horses = [].concat(horses);
    rest_horses.splice(i, 1)

    // Recursive call, continue generating permutations for remaining horses
    // Equivalent to nested for loops with self, scramble order
    permutate(rest_horses, new_result);
  }
}


function compare(t, q) {
  // Tianji's race times
  let t_won_cnt = 0;
  for (let i = 0; i < t.length; i++) {
    if (t_horses_time[t[i]] < q_horses_time[q[i]]) t_won_cnt++;
  }

  // Time comparison, this time is best of three
  if (t_won_cnt > (t.length / 2)) {
    console.log("Tianji wins!")
  } else {
    console.log("King wins!")
  }
}

// Start calculation
permutate(t_horses, [])

Tianji’s winning probability is 1/6

How Does Brute Force Password Cracking Use Permutation Thinking?

Brute force cracking is to try all possible passwords once

Assume there’s a 4-letter password, each digit is lowercase letter between a~e. Can you write code to brute force crack this password? (Hint: According to repeatable permutation rule, generate all possible 4-letter passwords.)

let words = ['a','b','c','d','e']
let passwords = []

function getPassword(words,result){
  // Enough 4, this branch ends
  if(result.length == 4){
    passwords.push(result)
    console.log(result);
    return;
  }

  // Not enough 4, continue taking
  for (let index = 0; index < words.length; index++) {
    let a = [].concat(words);
    let b = [].concat(result);
    b.push(a[index])
    a.splice(index,1)
    getPassword(a,b)
  }
}

getPassword(words,[])
console.log(passwords)

Permutation can help us generate many possibilities. Because of this characteristic, permutation’s most use is exhaustive method, i.e., list all possible situations, verify one by one, then see which situations meet conditions for solution.

Article Link:

/en/archive/math-permutation-solve-tianji-horse-racing-and-password-cracking/

# Related Articles