数字序列,不重复相邻数字

时间:2018-08-21 10:23:46

标签: algorithm math number-theory

我想获得一个数字序列,该序列不包含在给定底数的情况下在某个底数中重复的后续数字。我用一个例子说明自己:在10的底数中,序列将为0、1,...,10、12,...,21、23等。 ,12 55 68, 22 0等

或者,等效地,我想说以6为底的数字521包含重复的重复项(以10为底的521 =以6为底的 222 5)。

是否有一种方法可以对十进制数字进行迭代,以使其在给定基数中的表示形式不包含任何重复的重复项?我的意思是,以6为底数,在20之后的(十进制)数字是22(因为20是32,而22是34)。

当然,所有这些操作都无需在给定基数下进行索引转换并检查是否有重复的数字。

[编辑] 有一种方法可以判断十进制数(可以说是索引)在某个基数中是否有重复的连续数字,以便在生成序列时可以跳过它?

3 个答案:

答案 0 :(得分:1)

生成一系列数字

要检查某个数字是否在某个基数中包含相等的相邻数字,请在下面进一步查看我的初步答案。如果要生成一定范围内相邻数字不相等的数字范围,最好将支票内置到生成器中,而不是检查所有数字。

下面的代码示例创建一个以特定底数为单位的每个数字的值的表,并将数字零创建为一个数字数组。具有相同基数的每个后续呼叫将递增该数字,同时跳过相等的相邻数字。

function next(base, reset) {
    // If this is the first call, or if the base has been changed, a
    // new table with the values of digits in this base is generated.
    if (base !== next.base) {
        initialize_table();
        reset = true;
    }
    // If reset flag is set, re-initialize array of digits to zeros.
    if (reset) {
        initialize_digits();
        return 0;
    }
    increment_digits();
    return calculate_value();

    // The distinct parts of the algorithm have been split into 
    // seperate functions for clarity; for speed, integrate them.
    function initialize_table() {
        // The base, digit value table and array of digits are stored
        // as static variables, to be re-used between function calls.
        next.base = base;
        next.table = [];
        // Set a maximum for the values in the table (here it's 32-bit
        // unsigned integers); this determines the size of the table.
        // No overflow checking is done; add this if required.
        for (var pos = 0, val = 1, step = 1; val < 4294967296; pos++){
            next.table[pos] = [0];
            for (var digit = 1; digit < base; digit++, val += step) {
                next.table[pos][digit] = val;
            }
            step = val;
        }
    }
    function initialize_digits() {
        next.digit = [];
        for (var pos = 0; pos < next.table.length; pos++) {
            next.digit[pos] = 0;
        }
    }
    function increment_digits() {
        // Find the lowest digit that can be incremented.
        // No overflow checking is done; add if required.
        var pos = 0;
        while (next.digit[pos] == base-1 ||
               next.digit[pos] == base-2 &&
               next.digit[pos+1] == base-1) {
            ++pos;
        }
        // Add 1, or 2 if adding 1 would equal the next digit.
        while (++next.digit[pos] == next.digit[pos+1]);
        // Set the lower digits to 0 or 1 alternatingly.
        for (pos-- ; pos >= 0; pos--) {
            next.digit[pos] = (next.digit[pos+1] == 0) ? 1 : 0;
        }
    }
    function calculate_value() {
        // Look up value for each digit in the table and add up.
        var val = 0;
        for (pos = 0; pos < next.digit.length; pos++) {
            val += next.table[pos][next.digit[pos]];
        }
        return val;
    }
}

for (var b = 9; b > 1; b--) {
    document.write(b + " &rarr; ");
    for (var i = 0; i < 25; i++)
        document.write(next(b) + " ");
    document.write("...<br>");
}

查看单个号码

最有效的方法(特别是如果您要检查具有相同基数的多个数字)将是首先制作一个该基数中每个数字的值的表格,然后从高到低检查该数字。这样,您可以避免除法和取模,这在计算上很昂贵,并且您可以在找到两个相同的数字后立即返回,而不必进行完整的基数转换。


假设基数为6,数字是16位无符号整数,最大值为65535,则该表为:

position: 0     1     2     3     4     5     6
digit:
  0  ->   0     0     0     0     0     0     0
  1  ->   1     6    36   216  1296  7776 46656
  2  ->   2    12    72   432  2592 15552     -
  3  ->   3    18   108   648  3888 23328     -
  4  ->   4    24   144   864  5184 31104     -
  5  ->   5    30   180  1080  6480 38880     -

假设我们正在检查号码49876;我们首先检查位置6的数字值:

49876 >= 46656 ? yes
-> digit at position 6 is 1

然后我们从49876中减去46656,得到3220,然后继续检查位置5处的数字值:

3220 >= 38880 ? no
3220 >= 31104 ? no
3220 >= 23328 ? no
3220 >= 15552 ? no
3220 >=  7776 ? no
-> digit at position 5 is 0

此数字与前一个数字不同。我们不从数字中减去任何数字,因为数字为零;然后我们转到位置4的数字值:

3220 >= 6480 ? no
3220 >= 5184 ? no
3220 >= 3888 ? no
3220 >= 2592 ? yes
-> digit at position 4 is 2

此数字也与前一个数字不同。我们从3220减去2592得到628;然后我们转到位置3的数字值:

628 >= 1080 ? no
628 >=  864 ? no
628 >=  648 ? no
628 >=  432 ? yes
-> digit at position 3 is 2

这与前一位数字相同,因此我们知道该数字在基数6中有两个相同的相邻数字。


该表只能使用加法来构建,并且相对较小(最多16×16 = 256个元素,用于检查base-16中的64位整数),并且可以重复使用该表来检查同一位中的多个数字基础。仅通过比较和减法即可检查数字。绝对没有模,除,甚至乘法运算,因此它应该非常快。

如果数字包含相同的相邻数字,则算法可以在检查所有数字之前返回答案。如果没有相同的相邻数字,则检查所有数字,这实际上是完整的基数转换;但至少可以有效完成

答案 1 :(得分:0)

递归迭代生成器在数字上设置结果数组,并逐位回溯将给出数字的自然顺序。

生成器是具有类成员的类:

int length;
int curVal[10];
boolean digits[10];

此处curVal包含部分准备好的编号。

较低的索引对应于较高的数字位置,而pos是从高到低的位置从0开始的索引。

数字是一个集合(如果需要,则为布尔数组),以跟踪所使用的数字。

Generator驱动程序函数原型如下:

void generate(int pos)

在其中进行从0到9的简单循环,如果数字未设置在数字中,则将其放入,添加到curVal [pos]并调用generate(pos + 1)。返回后,从set和curVal中删除当前数字。

填入最后一位数字后,打印/排队编号以输出流/队列/列表。

除了curVal之外,您还可以跟踪二进制表示形式。每次将数位放入curVal时,都要预先计算10 ^ i并将precalc [length-pos-1] *位数添加到二进制值中,如果将其拉回,则减去该值。

另外,您需要分别处理第一位数字,因为最高位数字不能为0。自然地,可以在主循环内部以适当的长度完成

答案 2 :(得分:0)

这是我能想到的最简单的解决方案:

 private boolean check(long t, long base) {
    long x = -1;
    long y;
    while (t > 0) {
      y = t % base;
      if (y == x) {
        return false;
      }
      x = y;
      t = t / base;
    }
    return true;
  }