组合学:使用10个数字与52个字母生成1477万个不重复的4位串码V4衍生版本

 一.主要思想(进位制思想与移位思想):

{
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
        , "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"
        , "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
}

1).进位制思想

将0-9-a-Z,62个字符做为一个数制表系统,存入一个数组,既62位数字进制,逢"Z"数制进一位,好比9进一位到10。
4位串码前位不够补0,累加的操作只需要将末位往后移动一位循环移动。

由进位制思想可以推出,假如数制表长度为n,在n个无重复元素集合中,取m个元素做组合(长度为x,组合时可复用单个元素),组成为不同的串码组合,可以有n的m次方(n^m或n^x)种组合,即:

C(n,m)=n^m或=n^x;

若 m=4,则C(n,m)=62^4=14776336;//≈1477万

若 m=5,则C(n,m)=62^5=916132832;//≈9亿

若 m=6,则C(n,m)=62^5=56800235584;//≈568亿

以此类推。

2).移位思想

比如:
已知串码[a][b][c][d]
求下一位串码
将末位[d]往后移一位得到:[e]
故求得下一位串码是[a][b][c][e]  

二.实现思路

方式1
输入参数:当前串码
输出:新的串码

方式2
根据10进制数值,换算出指定长度的62进制的数串

三.下面走 java代码实现( CombinationCodeV4衍生版本 ) 

/**
 * Copyright (C), 2000-2021, XXX有限公司
 * FileName: CombinationCodeV4衍生版本
 * Author: wangyetao
 * Date: 21-10-23 21:00:00
 * Description:使用10个数字与52个字母生成n个不重复的n位串码,相当于62位数进制
 * 将0-9-A-z,62个字符做为一个进制系统,存入一个数组,既62位数字进制,4位串码前位不够补0。
 * 累加的操作只需要将末位往后移动一位循环移动,逢"Z"数制进一位。好比9进一位到10。
 * <p>
 * 比如:
 * * 已知串码[a][b][c][d]
 * * 求下一位串码
 * * 将末位[d]往后移一位得到:[e]
 * * 故求得下一位串码是[a][b][c][e]
 * 主要思路:
 * * 补位+进位+递归
 * <p>
 * History:
 * <author> <time> <version> <desc>
 * 作者姓名 修改时间 版本号 版本描述
 * wangyetao,21-10-21 15:29:22,CombinationNumberV2,版本描述
 * wangyetao,21-10-23 08:30:24,CombinationNumberV3,进位逻辑改用[递归]实现
 * wangyetao,21-10-23 21:00:00,CombinationCodeV4衍生版本,添加next方法
 */
package simple.callback.cryptographyalgorithm;

/**
 * @ClassName: CombinationNumberV3
 * @Description: java类描述
 * @Author: wangyetao
 * @Date: 21-10-23 08:30:24
 */
public class CombinationCodeV4 {

    //数制表
    private static String[] digitalsystemtable = {
            "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
            , "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"
            , "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
    };
    private static int DIGITALMETER_LENGTH = digitalsystemtable.length;//数制表长度
    private int defaultDigit = 4;//数位默认长度
    public static int countIndex = 0;//10进制数字记数器


    /**
     * 根据当前串码获取下一位串码
     *
     * @param digit          数位长度
     * @param currentCodeStr 当前串码
     * @return 获取下一位串码
     */
    public String generateNextCode(int digit, String currentCodeStr) {
        defaultDigit = digit;
        String[] digitalTempArr = currentCodeStr.split("");
        String[] digitalArr = new String[defaultDigit];

        //串码检测
        boolean isCompliance = filterCharacter(currentCodeStr);
        if (!isCompliance) {
            return "不合规的串码参数";
        }
        //补位检测
        digitalArr = doComplement(digitalTempArr);

        //保存单独位值
        String[] singleDigital = new String[defaultDigit];
        for (int i = 0; i < digitalArr.length; i++)
            singleDigital[i] = digitalArr[i];

        //进位开关
        boolean[] singleCarryup = new boolean[defaultDigit];
        int carryupLength = singleCarryup.length;
        for (int i = 0; i < carryupLength; i++)
            singleCarryup[i] = false;


        //进位思想主要逻辑递归代码
        singleDigital = recursiveNext(singleDigital, singleCarryup, 1);

        countIndex++;
        //拼接单独位值
        StringBuffer tmpBuff = new StringBuffer();
        for (String tmp : singleDigital)
            tmpBuff.append(tmp);

        return tmpBuff.toString();
    }

    /**
     * 数制运算与递归进位运算
     *
     * @param inSingleDigital 单独位值
     * @param inSingleCarryup 进位开关
     * @param step            步长为1
     */
    private String[] recursiveNext(String[] inSingleDigital, boolean[] inSingleCarryup, int step) {
        String[] singleDigital = inSingleDigital;
        boolean[] singleCarryup = inSingleCarryup;

        //步长控制A处:步长不能超过进位开关数长度;递归出口条件;
        //当步长大于进位开关数长度时,说明此时所有的位值均须进位,此时首位已到达最大值,无须再进位,归为0,进入下一轮。
        if (step <= singleDigital.length) {
            int inBinary62Index = indexOfDigitalsystemtable(singleDigital[defaultDigit - step]);
            inBinary62Index++;//下标后移一位

            if (inBinary62Index > DIGITALMETER_LENGTH - 1) {//进位检测
                singleDigital[defaultDigit - step] = digitalsystemtable[0];//数制进位前,本位归0
                singleCarryup[singleCarryup.length - step] = true;//打开开关,数制进一位

                if (singleCarryup[singleCarryup.length - step]) {

                    step++;
                    recursiveNext(singleDigital, singleCarryup, step);//进位递归
                }
            } else {
                singleDigital[defaultDigit - step] = digitalsystemtable[inBinary62Index];//单独位值取后移一位的值,即"+1"
            }
        }

        return singleDigital;
    }

    /**
     * 补位,前位不够补0
     *
     * @param inDigitalTempArr
     * @return targetArr
     */
    private String[] doComplement(String[] inDigitalTempArr) {
        String[] inTempArr = inDigitalTempArr;//[1][2][3]
        String[] targetArr = new String[defaultDigit];//[][][][]
        int differ = targetArr.length - inTempArr.length;

        //补位,前位不够补0
        if (inTempArr.length < targetArr.length) {
            //1)保存已有的值到目标数组
            for (int i = 0; i < inTempArr.length; i++)
                targetArr[i + differ] = inTempArr[i];

            //2)补位
            for (int i = 0; i < differ; i++)
                targetArr[i] = "0";

        } else {

            for (int i = 0; i < defaultDigit; i++)
                targetArr[i] = inTempArr[i];
        }
        return targetArr;
    }

    /**
     * 根据当前串码获取下一位串码
     *
     * @param digit          数位长度
     * @param currentCodeStr 当前串码
     * @return 获取下一位串码
     */
    private String next(int digit, String currentCodeStr) {
        return generateNextCode(digit, currentCodeStr);
    }


    /**
     * 此方法无须传入当前串码,方法内根据countIndex生成下一位串码
     * 仅适用在全局单例环境下,由10进制数字转换出62进制的数字串码
     *
     * @param digit 数位长度
     * @return 串码
     */
    public String next(int digit) {
        defaultDigit = digit;
        //将10进制数字转换出62进制的数字串码
        String _10_62code = BinaryConversion._10_to_62(countIndex, defaultDigit);
        String[] digitalTempArr = _10_62code.split("");
        String[] digitalArr = new String[defaultDigit];

        //补位检测
        digitalArr = doComplement(digitalTempArr);

        //保存单独位值
        String[] singleDigital = new String[defaultDigit];
        for (int i = 0; i < digitalArr.length; i++)
            singleDigital[i] = digitalArr[i];

        //进位开关
        boolean[] singleCarryup = new boolean[defaultDigit];
        int carryupLength = singleCarryup.length;
        for (int i = 0; i < carryupLength; i++)
            singleCarryup[i] = false;


        //进位思想主要逻辑递归代码
        singleDigital = recursiveNext(singleDigital, singleCarryup, 1);

        countIndex++;
        //拼接单独位值
        StringBuffer tmpBuff = new StringBuffer();
        for (String tmp : singleDigital)
            tmpBuff.append(tmp);

        return tmpBuff.toString();
    }


    /**
     * 根据10进制的数值生成指定长度的62进制的对应码
     *
     * @param digit  数位长度
     * @param _10num 10进制数值
     * @return
     */
    public String next(int digit, int _10num) {
        defaultDigit = digit;
        //将10进制的数值转换出62进制的数字串码
        String _10_62code = BinaryConversion._10_to_62(_10num, defaultDigit);
        String[] digitalTempArr = _10_62code.split("");
        String[] digitalArr = new String[defaultDigit];

        //补位检测
        digitalArr = doComplement(digitalTempArr);

        //保存单独位值
        String[] singleDigital = new String[defaultDigit];
        for (int i = 0; i < digitalArr.length; i++)
            singleDigital[i] = digitalArr[i];

        //进位开关
        boolean[] singleCarryup = new boolean[defaultDigit];
        int carryupLength = singleCarryup.length;
        for (int i = 0; i < carryupLength; i++)
            singleCarryup[i] = false;


        //进位思想主要逻辑递归代码
        singleDigital = recursiveNext(singleDigital, singleCarryup, 1);

        //拼接单独位值
        StringBuffer tmpBuff = new StringBuffer();
        for (String tmp : singleDigital)
            tmpBuff.append(tmp);

        return tmpBuff.toString();
    }

    /**
     * 检索值在数制表中的下标
     *
     * @param binaryValue
     * @return 下标
     */
    private int indexOfDigitalsystemtable(String binaryValue) {
        int sourceIndex = -1;

        for (int i = 0; i < DIGITALMETER_LENGTH; i++) {
            if (binaryValue.equals(digitalsystemtable[i])) {
                sourceIndex = i;
                break;
            }
        }
        return sourceIndex;
    }

    /**
     * 判断是否是合规串码
     *
     * @param currentCodeStr 串码
     * @return
     */
    private boolean filterCharacter(String currentCodeStr) {
        boolean isCompliance = true;
        String[] digitalTempArr = currentCodeStr.split("");
        for (int i = 0; i < digitalTempArr.length; i++) {
            if (indexOfDigitalsystemtable(digitalTempArr[i]) < 0) {
                isCompliance = false;
                break;
            }
        }
        return isCompliance;
    }


    /**
     * 测试用例
     *
     * @param args
     */
    public static void main(String[] args) throws InterruptedException {
        CombinationCodeV4 combinationNumber = new CombinationCodeV4();

        //DEMO_TEST1
        /*String initCode = "0000";
        int count = 0;
        count++;
        long startTime = System.currentTimeMillis();
        System.out.println(initCode);
        while (!initCode.equals("ZZZZ")) {
            initCode = combinationNumber.generateNextCode(4, initCode);
            count++;
            System.out.println(initCode);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("count=" + count + ",耗时" + (endTime - startTime) + "ms");*/


        //DEMO_TEST2
        /*System.out.println(combinationNumber.generateNextCode(5, "0000"));
        System.out.println(combinationNumber.generateNextCode(5, "ahzz"));
        System.out.println(combinationNumber.generateNextCode(5, "YYYY"));
        System.out.println(combinationNumber.generateNextCode(5, "ZZYZ"));
        System.out.println(combinationNumber.generateNextCode(5, "ZZZY"));
        System.out.println(combinationNumber.generateNextCode(5, "ZZZZ"));
        System.out.println(combinationNumber.generateNextCode(5, "ZZZZZZZZZZ"));*/

        //DEMO_TEST3
        /*System.out.println(combinationNumber.generateNextCode(10, "000"));
        System.out.println(combinationNumber.generateNextCode(10, "00"));
        System.out.println(combinationNumber.generateNextCode(10, "0"));
        System.out.println(combinationNumber.generateNextCode(10, ""));
        System.out.println(combinationNumber.generateNextCode(10, "000-"));
        System.out.println(combinationNumber.generateNextCode(10, "-00-"));
        System.out.println(combinationNumber.generateNextCode(10, "0-0-"));
        System.out.println(combinationNumber.generateNextCode(10, "--0-"));
        System.out.println(combinationNumber.generateNextCode(10, "----"));
        System.out.println(combinationNumber.generateNextCode(10, "ZZHZAA"));*/

        //DEMO_TEST4
        /*String initCode = "0000";
        int count = 0;
        count++;
        long startTime = System.currentTimeMillis();
        System.out.println(initCode);
        while (!initCode.equals("ZZZZZZZZZZ")) {
            initCode = combinationNumber.generateNextCode(10, initCode);
            count++;
            System.out.println(initCode);
            Thread.sleep(100);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("count=" + count + ",耗时" + (endTime - startTime) + "ms");*/

        //DEMO_TEST4_2
        String initCode = "0";
        int count = 0;
        count++;
        long startTime = System.currentTimeMillis();
        System.out.println(initCode);
        while (!initCode.equals("ZZZZ")) {
            initCode = (String) combinationNumber.next(4);
            count++;
            System.out.println(initCode);
            Thread.sleep(100);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("count=" + count + ",耗时" + (endTime - startTime) + "ms");

        //DEMO_TEST4_3
        /*int counter = 0;
        long startTime = System.currentTimeMillis();
        while (counter != 14776335) {
            System.out.println(combinationNumber.next(4,counter));
            counter++;
            Thread.sleep(100);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("count=" + counter + ",耗时" + (endTime - startTime) + "ms");*/

        /*//DEMO_TEST5
        FileOutputStream fos = new FileOutputStream("/home/wangyetao/Documents/CombinationNumberV2Test_DEMO_TEST5.log");

        String initCode = "0000";
        int count = 0;
        count++;
        long startTime = System.currentTimeMillis();
        System.out.println(combinationNumber.generateNextCode(10, initCode));
        fos.write((initCode + "\n").getBytes());
        while (!initCode.equals("ZZZZ")) {
            initCode = combinationNumber.generateNextCode(10, initCode);
            count++;
            System.out.println(initCode);
            fos.write((initCode + "\n").getBytes());
        }
        fos.close();

        long endTime = System.currentTimeMillis();
        System.out.println("count=" + count + ",耗时" + (endTime - startTime) + "ms");*/
    }

}

进制转换工具类 BinaryConversion.java

/**
 * Copyright (C), 2000-2021, XXX有限公司
 * FileName: BinaryConversion
 * Author: wangyetao
 * Date: 21-10-23 21:58:38
 * Description: 进制转换
 * History:
 * <author> <time> <version> <desc>
 * 作者姓名 修改时间 版本号 版本描述
 */
package simple.callback.cryptographyalgorithm;

import java.util.Stack;

/**
 * @ClassName: BinaryConversion
 * @Description: 进制转换工具类
 * @Author: wangyetao
 * @Date: 21-10-23 21:58:38
 */
public class BinaryConversion {

    private static char[] charSet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();

    /**
     * 将10进制转化为62进制
     *
     * @param number
     * @param length 转化成的62进制长度,不足length长度的话高位补0,否则不改变什么
     * @return
     */
    public static String _10_to_62(long number, int length) {
        Long rest = number;
        Stack<Character> stack = new Stack<Character>();
        StringBuilder result = new StringBuilder(0);
        while (rest != 0) {
            stack.add(charSet[new Long((rest - (rest / 62) * 62)).intValue()]);
            rest = rest / 62;
        }
        for (; !stack.isEmpty(); ) {
            result.append(stack.pop());
        }
        int result_length = result.length();
        StringBuilder temp0 = new StringBuilder();
        for (int i = 0; i < length - result_length; i++) {
            temp0.append('0');
        }

        return temp0.toString() + result.toString();

    }


    /**
     * 将62进制转换成10进制数
     *
     * @param ident62
     * @return
     */
    private static String convertBase62ToDecimal(String ident62) {
        int decimal = 0;
        int base = 62;
        int keisu = 0;
        int cnt = 0;

        byte ident[] = ident62.getBytes();
        for (int i = ident.length - 1; i >= 0; i--) {
            int num = 0;
            if (ident[i] > 48 && ident[i] <= 57) {
                num = ident[i] - 48;
            } else if (ident[i] >= 97 && ident[i] <= 122) {
                num = ident[i] - 97 + 10;
            } else if (ident[i] >= 65 && ident[i] <= 90) {
                num = ident[i] - 65 + 10 + 26;
            }
            keisu = (int) java.lang.Math.pow((double) base, (double) cnt);
            decimal += num * keisu;
            cnt++;
        }
        return String.format("%08d", decimal);
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //System.out.println("62System=" + _10_to_62(Integer.parseInt("35174605"), 5));
        //System.out.println("10System=" + convertBase62ToDecimal("2NaWL"));

        System.out.println("62System=" + _10_to_62(Integer.parseInt("14776335"), 4));
        System.out.println("10System=" + convertBase62ToDecimal("ZZZZ"));
    }


}

在此记录与总结,2021年 10月 24日 星期日 10:39:19 CST。

上一篇:【算法千题案例】⚡️每日LeetCode打卡⚡️——52.两个数组的交集


下一篇:NOIP2008 提高组题解