Набор Алгоритмов

Статья рассматривывает алгоритм перебора по алфавиту. Статья является основой для классов программ для Полного перебора по алфавиту и с его использованием. Полный перебор по алфавиту представляет собой выдачу строк от начальной позиции до конечной из символов, заданных в начальном алфавите. Приведены примеры реализации на языке Java. Перебирать так же можно не только строковые значения, но и другие типы данных.

Алгоритмы - Полный перебор по алфавиту.

1. Описание алгоритма.

Пусть у нас есть начальный алфавит:

Рис. 1. Пример начального алфавита.

В алфавите буквы пронумерованы. К примеру, от нуля.

Пусть у нас есть массив алфавита с элементами x=>y (индекс x соответствует символу y).

Массив алфавита: [0=>"A", 1=>"B", 2 => "C", 3 => "D"]

Рассмотрим на примере этого алфавита пример полного перебора по алфавиту.

Анимация. 1. Пример алгоритма полного перебора по алфавиту, без расширения границ при переполнении.

Примеры значий шагов алгоритма:

Таблица 1. Пример шагов алгоритма.

Шаг Значения индексов Буква по индесу
1 0000 AAAA
2 0001 AAAB
3 0002 AAAC
4 0003 AAAD
5 0010 AABA
6 0011 AABB
... ... ....
X 2330 CDDA
X+1 2331 CDDB
... ... ...
N-1 3332 DDDC
N 3333 DDDD

Шаги алгоритма получения следующего элемента:

2. Блок-схема алгоритма.

Приведём блок-схемы для всего процесса генерации. Алгоритм содержат следующие функции:

2.1. Блок-схема конструктора.

Входные данные: minValue — нижняя граница перебора, maxValue — верхняя граница перебора, alphabet — алфавит для перебора.

Выходные данные: value — текущее значение (копия minValue), сохранённая minValue, сохранённая maxValue, сохранённый alphabet.

Рис. 2. Блок-схема конструктора.

2.2. Блок-схема метода hasNext.

Входные данные: minValue — нижняя граница перебора, maxValue — верхняя граница перебора, value — текущее значение, alphabet — алфавит для перебора.

Выходные данные: Значение Истина или Ложь для ответа на вопрос "Есть ли следующий элемент?".

Рис. 3. Блок-схема метода hasNext.

2.3. Блок-схема метода hasPrevious.

Входные данные: minValue — нижняя граница перебора, maxValue — верхняя граница перебора, value — текущее значение, alphabet — алфавит для перебора.

Выходные данные: Значение Истина или Ложь для ответа на вопрос "Есть ли предыдущий элемент?".

Рис. 4. Блок-схема метода hasPrevious.

2.4. Блок-схема метода next.

Входные данные: minValue — нижняя граница перебора, maxValue — верхняя граница перебора, value — текущее значение, alphabet — алфавит для перебора.

Выходные данные: перевод состояния value.

Рис. 5. Блок-схема метода next.

2.5. Блок-схема метода previous.

Входные данные: minValue — нижняя граница перебора, maxValue — верхняя граница перебора, value — текущее значение, alphabet — алфавит для перебора.

Выходные данные: перевод состояния value.

Рис. 6. Блок-схема метода previous.

2.6. Блок-схема метода getValue.

Входные данные: minValue — нижняя граница перебора, maxValue — верхняя граница перебора, value — текущее значение, alphabet — алфавит для перебора.

Выходные данные: Преобразование value в строку символов.

Рис. 7. Блок схема метода getValue.

3. Пример на Java.

Пример реализации полного перебора по афавиту на языке Java 12 SE.

Bruteforce.java:

package Bruteforce;

import java.util.ArrayList;

public class Bruteforce {

    protected ArrayList<Integer> minValue;
    protected ArrayList<Integer> value;
    protected ArrayList<Integer> maxValue;
    protected ArrayList<Character> alphabet;

    public Bruteforce(String alphabet, ArrayList<Integer> minValue, ArrayList<Integer> maxValue) {
        /* Переменные с префиксом this - это защищённые поля класса. Без префикса - параметры конструктора. */

        // Запоминаем алфавит.
        this.alphabet = new ArrayList<Character>();

        for(int i = 0; i < alphabet.length(); i++)
            this.alphabet.add(alphabet.charAt(i));

        // Проверка на то, что размер minValue меньше или равно maxValue.
        if(minValue.size() < maxValue.size()) {
            // Запоминаем начальное и конечное значения.
            this.minValue = minValue;
            this.maxValue = maxValue;
            this.value = (ArrayList<Integer>) minValue.clone();
        }
        else {

            if(minValue.size() > maxValue.size())
                throw new IllegalArgumentException();

            for(int i = 0; i < maxValue.size(); i++) {
                if(minValue.get(i).intValue() < maxValue.get(i).intValue())
                    break;
                if(minValue.get(i).intValue() > maxValue.get(i).intValue())
                    throw new IllegalArgumentException();
            }

            // Запоминаем начальное и конечное значения.
            this.minValue = minValue;
            this.maxValue = maxValue;
            this.value = (ArrayList<Integer>) minValue.clone();
        }
    }

    public boolean hasNext() {
        if(value.size() < maxValue.size())
            return true;

        int i;
        boolean result = false;
        for(i = maxValue.size() - 1; i > -1; i--) {
            if(value.get(i).intValue() > maxValue.get(i).intValue())
                break;
        }

        for(int j = maxValue.size() - 1; j > -1 && !result; j--) {
            if((value.get(j).intValue() != maxValue.get(j).intValue()))
                result = true;
        }

        if(i == -1)
            return result;
        else
            return false;
    }

    public boolean hasPrevious() {
        if(value.size() > minValue.size())
            return true;

        int i;
        boolean result = false;
        for(i = value.size() - 1; i > -1; i--) {
            if(value.get(i).intValue() < minValue.get(i).intValue())
                break;
        }

        for(int j = value.size() - 1; j > -1 && !result; j--) {
            if((value.get(j).intValue() != minValue.get(j).intValue()))
                result = true;
        }

        if(i == -1)
            return result;
        else
            return false;
    }

    public void next() {
        value.set(0, value.get(0) + 1);

        int startSize = value.size();
        for(int i = 0; i < startSize; i++) {
            if(value.get(i) >= alphabet.size()) {
                value.set(i, 0);
                if(i == value.size() - 1) {
                    value.add(0);
                }
                else {
                    value.set(i + 1, value.get(i + 1) + 1);
                }
                
            }
        }
    }

    public void previous() {
        int firstIndex = 0;
        int lastIndex = value.size() - 1;
        int lastAlphabetIndex = alphabet.size() - 1;
        value.set(firstIndex, value.get(firstIndex) - 1);

        for(int i = 0; i < value.size() - 1; i++) {
            if(value.get(i) <= -1) {
                value.set(i, lastAlphabetIndex);
                value.set(i+1, value.get(i+1) - 1);
            }
        }

        if(value.get(lastIndex) < 0) {
            ArrayList<Integer> resultArray = new ArrayList<Integer>();
            for(int j = lastIndex - 1; j > -1; j--) {
                resultArray.add(lastAlphabetIndex);
            }
            value = resultArray;
        }
    }

    public String getValue() {
        String result = "";

        for(int i = value.size() - 1; i > -1; i--) {
            result += alphabet.get(value.get(i));
        }

        return result;
    }

}

Далее пример для тестирования класса написанного выше.

TestBruteforce.java:

package Bruteforce;

import java.util.ArrayList;

public class TestBruteforce {

    public static void main(String[] args) {

        ArrayList<Integer> min = new ArrayList<Integer>(1);
        ArrayList<Integer> max = new ArrayList<Integer>(4);

        // min = [0]
        min.add(0);

        // max = [3, 3, 3, 3]
        for(int i = 0; i < 4; i++)
            max.add(3);

        Bruteforce counter = new Bruteforce("0123", min, max);

        System.out.println(counter.getValue());
        while(counter.hasNext()) {
            counter.next();
            System.out.println(counter.getValue());
        }

        while(counter.hasPrevious()) {
            counter.previous();
            System.out.println(counter.getValue());
        }

    }

}

Вывод теста:

0
1
2
3
00
01
02
03
10
...
23
30
31
32
33
000
001
002
003
010
011
012
013
020
021
022
...
331
332
333
0000
0001
0002
0003
...
0331
0332
0333
1000
1001
1002
1003
1010
1011
1012
1013
1020
1021
1022
1023
1030
...
2230
2231
2232
2233
2300
2301
2302
2303
2310
2311
2312
2313
2320
2321
2322
2323
2330
2331
2332
2333
3000
3001
3002
3003
...
3210
3211
3212
3213
3220
3221
3222
3223
3230
3231
3232
3233
3300
3301
3302
3303
3310
3311
3312
3313
3320
3321
3322
3323
3330
3331
3332
 3333
3332
3331
3330
3323
3322
3321
3320
3313
3312
3311
3310
3303
3302
3301
3300
...
2102
2101
2100
2033
2032
2031
2030
2023
2022
2021
2020
2013
2012
2011
2010
2003
2002
2001
2000
1333
1332
1331
1330
1323
1322
1321
...
0322
0321
0320
0313
0312
0311
0310
0303
0302
0301
0300
0233
0232
0231
0230
0223
0222
0221
0220
0213
0212
0211
0210
0203
0202
0201
0200
0133
...
0003
0002
0001
0000
333
332
331
330
323
...
300
233
232
231
230
223
222
221
220
213
212
211
210
203
202
201
200
133
132
131
130
123
122
121
120
113
112
111
110
103
102
101
100
033
032
031
030
023
022
021
020
013
012
011
010
003
002
001
000
33
32
31
30
23
22
21
20
13
12
11
10
03
02
01
00
3
2
1
0

4. Преимущества и Недостатки.

Не используйте данный алгоритм для взлома паролей.

Преимущества:

Недостатки: