Xnary (like binary but different) counting

后端 未结 3 551
感情败类
感情败类 2020-12-18 05:26

I\'m making a function that converts a number into a string with predefined characters. Original, I know. I started it, because it seemed fun at the time. To do on my own. W

相关标签:
3条回答
  • 2020-12-18 05:36

    Also known as Excel column numbering. It's easier if we shift by one, A = 0, ..., Z = 25, AA = 26, ..., at least for the calculations. For your scheme, all that's needed then is a subtraction of 1 before converting to Xnary resp. an addition after converting from.

    So, with that modification, let's start finding the conversion. First, how many symbols do we need to encode n? Well, there are 26 one-digit numbers, 26^2 two-digit numbers, 26^3 three-digit numbers etc. So the total of numbers using at most d digits is 26^1 + 26^2 + ... + 26^d. That is the start of a geometric series, we know a closed form for the sum, 26*(26^d - 1)/(26-1). So to encode n, we need d digits if

    26*(26^(d-1)-1)/25 <= n < 26*(26^d-1)/25   // remember, A = 0 takes one 'digit'
    

    or

    26^(d-1) <= (25*n)/26 + 1 < 26^d
    

    That is, we need d(n) = floor(log_26(25*n/26+1)) + 1 digits to encode n >= 0. Now we must subtract the total of numbers needing at most d(n) - 1 digits to find the position of n in the d(n)-digit numbers, let's call it p(n) = n - 26*(26^(d(n)-1)-1)/25. And the encoding of n is then simply a d(n)-digit base-26 encoding of p(n).

    The conversion in the other direction is then a base-26 expansion followed by an addition of 26*(26^(d-1) - 1)/25.

    So for N = 1000, we encode n = 999, log_26(25*999/26+1) = log_26(961.5769...) = 2.x, we need 3 digits.

    p(999) = 999 - 702 = 297
    297 = 0*26^2 + 11*26 + 11
    999 = ALL
    

    For N = 158760, n = 158759 and log_26(25*158759/26+1) = 3.66..., we need four digits

    p(158759) = 158759 - 18278 = 140481
    140481 = 7*26^3 + 25*26^2 + 21*26 + 3
    158759 = H        Z         V       D
    
    0 讨论(0)
  • 2020-12-18 05:37

    This appears to be a very standard "implement conversion from base 10 to base N" where N happens to be 26, and you're using letters to represent all digits.

    If you have A-Z as a 26ary value, you can represent 0 through (26 - 1) (like binary can represent 0 - (2 - 1).

    BZ = 1 * 26 + 25 *1 = 51

    The analogue would be:

    19 = 1 * 10 + 9 * 1 (1/B being the first non-zero character, and 9/Z being the largest digit possible).

    You basically have the right idea, but you need to shift it so A = 0, not A = 1. Then everything should work relatively sanely.

    0 讨论(0)
  • 2020-12-18 05:54

    In the lengthy answer by @Daniel I see a call to log() which is a red flag for performance. Here is a simple way without much complex math:

    function excelize(colNum) {
        var order = 0, sub = 0, divTmp = colNum;
        do {
            divTmp -= 26**order;
            sub += 26**order;
            divTmp = (divTmp - (divTmp % 26)) / 26;
            order++;
        } while(divTmp > 0);
    
        var symbols = "0123456789abcdefghijklmnopqrstuvwxyz";
        var tr = c => symbols[symbols.indexOf(c)+10];
        Number(colNum-sub).toString(26).split('').map(c=>tr(c)).join('');
    }
    

    Explanation:

    Since this is not base26, we need to substract the base times order for each additional symbol ("digit"). So first we count the order of the resulting number, and at the same time count the substract. And then we convert it to base 26 and substract that, and then shift the symbols to A-Z instead of 0-P.

    0 讨论(0)
提交回复
热议问题