1

Given that a number can contain only digits from 1 to 8 (with no repetition), and is of length 8, how can we hash such numbers without using a hashSet?

We can't just directly use the value of the number of the hashing value, as the stack size of the program is limited. (By this, I mean that we can't directly make the index of an array, represent our number).

Therefore, this 8 digit number needs to be mapped to, at maximum, a 5 digit number.

I saw this answer. The hash function returns a 8-digit number, for a input that is an 8-digit number.

So, what can I do here?

Mooncrater
  • 4,146
  • 4
  • 33
  • 62

2 Answers2

2

There's a few things you can do. You could subtract 1 from each digit and parse it as an octal number, which will map one-to-one every number from your domain to the range [0,16777216) with no gaps. The resulting number can be used as an index into a very large array. An example of this could work as below:

function hash(num) {
  return parseInt(num
    .toString()
    .split('')
    .map(x => x - 1), 8);
}

const set = new Array(8**8);
set[hash(12345678)] = true;
// 12345678 is in the set

Or if you wanna conserve some space and grow the data structure as you add elements. You can use a tree structure with 8 branches at every node and a maximum depth of 8. I'll leave that up to you to figure out if you think it's worth the trouble.


Edit: After seeing the updated question, I began thinking about how you could probably map the number to its position in a lexicographically sorted list of the permutations of the digits 1-8. That would be optimal because it gives you the theoretical 5-digit hash you want (under 40320). I had some trouble formulating the algorithm to do this on my own, so I did some digging. I found this example implementation that does just what you're looking for. I've taken inspiration from this to implement the algorithm in JavaScript for you.

function hash(num) {
  const digits = num
    .toString()
    .split('')
    .map(x => x - 1);
  const len = digits.length;
  const seen = new Array(len);
  let rank = 0;

  for(let i = 0; i < len; i++) {
    seen[digits[i]] = true;
    rank += numsBelowUnseen(digits[i], seen) * fact(len - i - 1);
  }

  return rank;
}

// count unseen digits less than n
function numsBelowUnseen(n, seen) {
  let count = 0;
  for(let i = 0; i < n; i++) {
    if(!seen[i]) count++;
  }
  return count;
}

// factorial fuction
function fact(x) {
  return x <= 0 ? 1 : x * fact(x - 1);
}
kamoroso94
  • 1,713
  • 1
  • 16
  • 19
0

kamoroso94 gave me the idea of representing the number in octal. The number remains unique if we remove the first digit from it. So, we can make an array of length 8^7=2097152, and thus use the 7-digit octal version as index.

If this array size is bigger than the stack, then we can use only 6 digits of the input, convert them to their octal values. So, 8^6=262144, that is pretty small. We can make a 2D array of length 8^6. So, total space used will be in the order of 2*(8^6). The first index of the second dimension represents that the number starts from the smaller number, and the second index represents that the number starts from the bigger number.

kamoroso94
  • 1,713
  • 1
  • 16
  • 19
Mooncrater
  • 4,146
  • 4
  • 33
  • 62