这个算法什么时候重复?

时间:2015-10-24 03:23:50

标签: java string algorithm

当我遇到以下算法时,我正在弄乱用Java操纵均匀长度字符串的不同方法。

给定一个字符串,重新排列如下:第一个字符,最后一个字符,第二个字符,倒数第二个字符等等。例如,字符串“test”将产生“ttes”,这将产生“tste” ,这将再次产生“测试”。

我开始尝试更长的字符串,假设每个字符串“循环”所需的迭代次数将等于其长度,就像“test”字符串的情况一样。然而,这种情况并非如此。在循环回来之前,每个经过测试的均匀长度的字符串都会看似随机的迭代次数。这是给定字符串长度的迭代次数的前几个值的简短表:

Table showing values for the described algorithm

正如您从前几个值中可以看到的那样,实际上没有一个易于找到的模式可以预测随机字符串循环所需的迭代次数。价值似乎只是跳到了整个地方。所以我的问题是,任何人都可以解释为什么迭代次数的行为与它一样吗?有没有办法预测给定长度的字符串所需的迭代次数是多少?

为了试着回答这个问题,我用Java编写了一个简短的程序来强制计算所需的迭代次数,希望通过更多的试验找到一个模式。我没有任何运气,但如果它有帮助,这里是代码(对于邋iness抱歉):

import java.util.Random;

public class mainClass
{
    //initialize global variables
    static String originalString = "";
    static Random randGen = new Random(System.currentTimeMillis());


    //main method
    public static void main(String[] args)
    {
        //add two new random characters to the original string
        originalString += Character.toString((char)(randGen.nextInt(99) + 32));
        originalString += Character.toString((char)(randGen.nextInt(99) + 32));

        //show the length of the string
        System.out.print(originalString.length() + "\t");

        //"decode" the string once to get it going and initialize count
        String test = decode(originalString);
        int count = 1;

        //"decode" the string until it is equal to the original string again
        while(!test.equals(originalString))
        {
            test = decode(test);
            count++;
        }

        //print out the number of iterations needed to "cycle around"
        System.out.println(count);

        //restart
        main(null);
    }

    public static String decode(String str)
    {
        //initialize result variable
        String result = "";

        //perform the rearranging process
        for(int i = 0; i < str.length() / 2; i ++)
        {
            result += Character.toString(str.charAt(i));
            result += Character.toString(str.charAt(str.length() - 1 - i));
        }

        //return the result
        return result;
    }
}

有人有什么想法吗?

2 个答案:

答案 0 :(得分:1)

要强制计算返回原始序列所需的迭代次数,您可以从一系列唯一数字开始,例如: 0,1,2,3,4,...,N,并重复应用该函数,直到您返回原始序列。

没有随机性可能会导致结果偏差,并且它不需要任何可能的证明,即如果一个值返回到其原始位置,则所有其他值也将具有。它也比String连接快得多。

此代码执行此操作。它也可以在奇数序列上完成。

public static void main(String[] args) {
    for (int len = 1; len <= 1000; len++) {
        int count = 0;
        short[] value = build(len);
        do {
            value = apply(value);
            count++;
        } while (! done(value));
        System.out.printf("%d\t%d%n", len, count);
    }
}
private static short[] build(int len) {
    short[] value = new short[len];
    for (int i = 0; i < len; i++)
        value[i] = (short)i;
    return value;
}
private static boolean done(short[] value) {
    for (int i = 0; i < value.length; i++)
        if (value[i] != i)
            return false;
    return true;
}
private static short[] apply(short[] input) {
    short[] output = new short[input.length];
    for (int i = 0; i < input.length; i++)
        output[i] = input[(i & 1) == 0 ? i / 2 : input.length - i / 2 - 1];
    return output;
}

如果你绘制结果,你得到这个:

Plot

正如您所看到的,结果非常分散,但有一些模式。

我不是数字向导,但这似乎与质数的“随机性”有些类似,因为有许多数论。

这就是为什么我建议在https://math.stackexchange.com/上提出这个问题,真正的数学向导在那里出现。也许他们可以提供更多的见解。

答案 1 :(得分:0)

第一个角色将始终保持在同一位置。 跟踪列表中第二个元素的位置就足够了 它将永远不会迭代超过n-1次,因为位置的变化应循环回到其原始位置,并且由于算法,每个字符将在一个循环中从一个位置移动到另一个位置。对于n = 10:

a b c d e f g h i j
a j b i c h d g e f
a f j e b g i d c h
a h f c j d e i b g
a g h b f i c e j d
a d g j h e b c f i
a i d f g c j b h e
a e i h d b f j g c
a c e g i j h f d b
a b c d e f g h i j

cycle pattern:
1 2 4 8 3 6 7 5 9 1

因此,无论位置流量如何,它都会以最多n-1个动作循环回来。

在某些情况下,周期较短:

a b c d e f g h i j k l m n o p
a p b o c n d m e l f k g j h i
a i p h b j o g c k n f d l m e
a e i m p l h d b f j n o k g c
a c e g i k m o p n l j h f d b
a b c d e f g h i j k l m n o p

1  2 4  8 15 1
3  6 12 7 14 3
5 10  11 9 13 5

(顺便说一句,这类似于^ x(mod m)= y,这取决于x和m是否相对为素数。在这种情况下,如果它们是相对素数,它将循环通过所有数字。)

但是,在我们的例子中,序列不是明确的,但你可以通过查看从位置1开始的字符来简化:

[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 1
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 2
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 4
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 8
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0] 16
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 11
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] 21
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 1

只要在它到达n / 2 + 1位置(11,在这种情况下)之前交换持续19次迭代,那么它将需要21次迭代。如果它以较少的速度返回,那么整个数组将花费多长时间。

因此,您可以根据交换算法确定从查看位置1并移动它所需解码的次数(但仅在出现&#39; 1&#39;时应用它)。需要O(n)来检查(最多n-1次移动),并省去交换整个阵列的麻烦。