我的校验和算法有什么问题?

时间:2013-09-07 22:05:59

标签: java algorithm

我正在为比赛做一些练习问题,我一直在研究这个算法。如果你想阅读整个问题here,我会给你一个简短的解释,因为这是一个很长的问题。

问题:

您必须通过将ID号插入校验和来验证ID号。在将ID插入算法之前,需要将ID转换为base-10。身份证号码以字母开头:

  

Z = 0,Y = 1,X = 2,W = 3,V = 4

我在将这些字母转换为base-10时遇到问题,我的转换代码很好,所以我会告诉你问题的下一部分:

第2部分:

获得基数为10的ID后,需要将其插入以下算法:

注意:每个ID号码必须是8位数字,0表示在一个至少8位数字之前。

checksum = F(0, d0) X F(1, d1) X F(2, d2) ...

简化:

checksum = F(n, dn) X F(n+1, dn) ...
where n is the index of the digit

这里最重要的是X不是操作*(乘法)。 X是后来定义的自己的操作。

注意:最重要的数字似乎是d7,但我不确定,问题不是很清楚。


以下是f(n1,n2),g(n)和运算符X的定义:

f(n1,n2)=

f(n1, n2)

g(n)=

g(n)

操作员X:

Operator X

我认为mod与我的代码中的%相同,我不确定是否还有其他mod操作我不熟悉。

我的结构

这就是我决定要解决问题的方法:

  1. 将基数为10的数字转换为int[8]
  2. int[8]的每个数字放到f(n, dn)
  3. 使用X运算符将它们组合在一起。
  4. 我的代码

    这是我的算法功能。如果它们在某处混淆,我可以对它们进行评论,但它们确实遵循上面列出的算法。

    /*
     * This will return the checksum of the id.
     * Formula: F(0, d0) X F(1, d1) ...
     * 
     * F(n, dn) where n is the current index.
     * X != * (multiply)!! X is a defined operator
     */
    public static int getChecksum(int[] id)
    {
        int result = 0;
    
        for(int x = 0;x < id.length;x++)
        {
            if(x == 0)
                result = fOfxd(x, id[x]);
            else{
                result = opX(result, fOfxd(x, id[x]));
            }
        }
    
        return result;
    }
    
    public static int gOfx(int x)
    {
        return GOFX[x];
    }
    
    public static int fOfxd(int x, int d)
    {
        switch(x)
        {
            case 0:
                return d;
            case 1:
                return gOfx(d);
            default:
                return fOfxd(x - 1, gOfx(d));
        }
    }
    
    public static int opX(int num1, int num2)
    {
        if(num1 < 5 && num2 < 5)
            return (num1 + num2) % 5;
        if(num1 < 5 && num2 >= 5)
            return (num1 + (num2 - 5)) % 5 + 5;
        if(num1 >= 5 && num2 < 5)
            return ((num1 - 5) - num2) % 5 + 5;
        return (num1 - num2) % 5;
    }
    
    public static final int[] GOFX = {1, 5, 7, 6, 2, 8, 3, 0, 9, 4};
    

    现在,这是我的main(String args[])代码:

    注意:您可以假设函数parseBase10toArray正常运行。我已经用问题中的输入/输出示例检查了它们。

    public static void main(String args[])
    {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    
        while(true)
        {
            int ids = 0; // how many ids are we checking?
            try
            {
                ids = Integer.parseInt(reader.readLine()); // get user input
    
                String[] list = new String[ids]; // will hold all of the ids
    
                for(int x = 0;x < list.length;x++)
                    list[x] = reader.readLine(); // reads all of the ids we will be checking
    
                for(int x = 0;x < list.length;x++) // lets check the ids individually now
                {
                    String stringID = list[x]; // the string representation of the id
                    int base10 = parseBase10(stringID);
                    int[] id = toArray(base10);
                    int checksum = getChecksum(id);
    
                    System.out.println(stringID);
                    System.out.println(base10);
                    System.out.println(Arrays.toString(id));
                    System.out.println(checksum);
    
                }
            }catch(Exception e){e.printStackTrace();}
            break;
        }
    }
    

    想自己编译吗?

    这是我的完整(未经编辑)代码:

    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.util.Arrays;
    
    public class Main 
    {
        public static void main(String args[])
        {
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    
            while(true)
            {
                int ids = 0; // how many ids are we checking?
                try
                {
                    ids = Integer.parseInt(reader.readLine()); // get user input
    
                    String[] list = new String[ids]; // will hold all of the ids
    
                    for(int x = 0;x < list.length;x++)
                        list[x] = reader.readLine(); // reads all of the ids we will be checking
    
                    for(int x = 0;x < list.length;x++) // lets check the ids individually now
                    {
                        String stringID = list[x]; // the string representation of the id
                        int base10 = parseBase10(stringID);
                        int[] id = toArray(base10);
                        int checksum = getChecksum(id);
    
                        System.out.println(stringID);
                        System.out.println(base10);
                        System.out.println(Arrays.toString(id));
                        System.out.println(checksum);
    
                    }
                }catch(Exception e){e.printStackTrace();}
                break;
            }
        }
    
        /*
         * This will return the checksum of the id.
         * Formula: F(0, d0) X F(1, d1) ...
         * 
         * F(n, dn) where n is the current index.
         * X != * (multiply)!! X is a defined operator
         */
        public static int getChecksum(int[] id)
        {
            int result = 0;
    
            for(int x = 0;x < id.length;x++)
            {
                if(x == 0)
                    result = fOfxd(x, id[x]);
                else{
                    result = opX(result, fOfxd(x, id[x]));
                }
            }
    
            return result;
        }
    
        public static int gOfx(int x)
        {
            return GOFX[x];
        }
    
        public static int fOfxd(int x, int d)
        {
            switch(x)
            {
                case 0:
                    return d;
                case 1:
                    return gOfx(d);
                default:
                    return fOfxd(x - 1, gOfx(d));
            }
        }
    
        public static int opX(int num1, int num2)
        {
            if(num1 < 5 && num2 < 5)
                return (num1 + num2) % 5;
            if(num1 < 5 && num2 >= 5)
                return (num1 + (num2 - 5)) % 5 + 5;
            if(num1 >= 5 && num2 < 5)
                return ((num1 - 5) - num2) % 5 + 5;
            return (num1 - num2) % 5;
        }
    
        /*
         * This will convert a number to an array equivalent of that number
         * The result will be 8 digites long with leading 0's if possible.
         * 
         * EX:
         * 12345 = {0, 0, 1, 2, 3, 4, 5, 6}
         */
        public static int[] toArray(int value)
        {
            int result[] = new int[8];
    
            for(int x = result.length - 1;x >= 0;x--)
            {
                result[x] = value % 10;
                value /= 10;
            }
    
            return result;
        }
    
        /*
         * converts a String sequence and converts it to a base 10 equivalent.
         * Z = 0, Y = 1, X = 2, W = 3, V = 4
         * 
         * EX:
         * YY = 11(base-5) = 6(base-10)
         */
        public static int parseBase10(String string) throws Exception
        {
            int multiplier = 1;
            int result = 0; // in base 10
    
            for(int x = string.length() - 1;x >= 0;x--)
            {
                char letter = string.charAt(x); // the letter we are parsing
                int value = -1; // initial value, set to -1 to check for parsing error
    
                for(int y = 0;y < VALUES.length;y++)
                    if(letter == VALUES[y])
                        value = y; // letter found in VALUES[]
    
                if(value == -1)
                    throw new Exception("Could not parse: " + letter); // the specified letter was not found
    
                result += (multiplier * value);
                /* ^^ this moves the value to the correct digit place by using a multiplier:
                 * EX:
                 * 
                 * current result: 45 (base-10)
                 * new value to parse: 2 (base-5)
                 * 45(base-10) + (2(base-5) * 25(base-10)) = 245 <-- correct output
                 */
    
                multiplier *= 5; // sets up multiplier for next value
            }
    
            return result;
        }
    
        public static final char[] VALUES = {'Z', 'Y', 'X', 'W', 'V'};
        public static final int[] GOFX = {1, 5, 7, 6, 2, 8, 3, 0, 9, 4};
    }
    

    以下是我提出问题的输入:

      

    6
      WYYXWVZXX
      YWYWYYXWVZYY
      YWYWYYXWVZYX
      YYZWYYXWVZYX
      YXXWYYXWVZXW
      XYXWYYXWXYY

    这是我得到的:

    WYYXWVZXX
    1274262
    [0, 1, 2, 7, 4, 2, 6, 2]
    2  *0*
    YWYWYYXWVZYY
    81352381
    [8, 1, 3, 5, 2, 3, 8, 1]
    0
    YWYWYYXWVZYX
    81352382
    [8, 1, 3, 5, 2, 3, 8, 2]
    4
    YYZWYYXWVZYX
    59868007
    [5, 9, 8, 6, 8, 0, 0, 7]
    0
    YXXWYYXWVZXW
    73539888
    [7, 3, 5, 3, 9, 8, 8, 8]
    5  *0*
    XYXWYYXWXYY
    22520431
    [2, 2, 5, 2, 0, 4, 3, 1]
    3  *0*
    

    你看到*0*的地方我应该得到0,但我得到了不同的价值。我的校验和算法搞砸了哪里?

    阅读所有内容可以随意要求澄清我的代码的任何部分。

2 个答案:

答案 0 :(得分:7)

BUG 1

错误很微妙。首先,问题中的数字描述是:d7 d6 ... d1 d0  这意味着,d0是十进制数的单位值。

然后,他们说F是左关联的,并将过程描述为:

F(0,d0) x F(1,d1) x F(2,d2) x ... x F(6,d6) x F(7,d7)

这意味着,您必须先向运营商F申请d0。但是当你创建int数组时,0索引处的元素是d7,因为在这种情况下顺序很重要,你会得到错误的结果。

要解决这个问题,你只需要反转你的int数组的十进制值表示。

BUG 2

第二个错误是在操作模5中。正如你可以在问题说明中看到的那样,他们说:

  

注意-4 mod 5 = 1。

因此,复制粘贴操作符x是错误的。改为:

public static int opX(int num1, int num2)
{
    if(num1 < 5 && num2 < 5)
        return (num1 + num2) % 5;
    if(num1 < 5 && num2 >= 5)
        return (num1 + (num2 - 5)+5) % 5 + 5;
    if(num1 >= 5 && num2 < 5)
        return ((num1 - 5) - num2+20) % 5 + 5;
    return (num1 - num2 +10) % 5;
}

你会得到预期的结果。

以下是修复了两个错误的结果:

1274262
[2, 6, 2, 4, 7, 2, 1, 0]
0
YWYWYYXWVZYY
81352381
[1, 8, 3, 2, 5, 3, 1, 8]
0
YWYWYYXWVZYX
81352382
[2, 8, 3, 2, 5, 3, 1, 8]
1
YYZWYYXWVZYX
59868007
[7, 0, 0, 8, 6, 8, 9, 5]
0
YXXWYYXWVZXW
73539888
[8, 8, 8, 9, 3, 5, 3, 7]
0
XYXWYYXWXYY
22520431
[1, 3, 4, 0, 2, 5, 2, 2]
0

修改

有关BUG 2的更一般解决方案,请查看Martijn Courteaux的答案。

答案 1 :(得分:3)

您的mod逻辑已损坏。该网站说:

  

请注意-4 % 5 = 1

在Java中,情况并非如此:(-4) % 5 == -4。因此,请实施您自己的mod(int a, int b)方法:

public static int mod(int a, int b)
{
    while (a < 0) a += b;
    while (a >= b) a -= b;
    return a;
}

@ durron597 建议的更高效的实现:

public static int mod(int a, int b)
{
    a %= b;
    return a < 0 ? a + b : a;
}

这非常重要,因为这里你会有负值 (例如:假设num1 = 5num2 = 4):

if(num1 >= 5 && num2 < 5)
    return ((num1 - 5) - num2) % 5 + 5;