Сообщение Re: Забавный факт про сложность пароля от 28.11.2022 2:29
Изменено 28.11.2022 2:40 vsb
Re: Забавный факт про сложность пароля
Если вдруг кому будет нужна правильная формула, то описание ниже:
Итак постановка задачи: у нас генерируется пароль из m групп символов длиной n символов, m <= n. Группы символов не пересекаются, размер каждой группы x_i символов. К примеру пароль длиной 8 из больших, маленьких букв и цифр, m = 3, n = 8, x = [26, 26, 10]. Также дополнительное условие — в пароле должен быть хотя бы один символ из каждой группы.
Вопрос — сколько таких паролей может быть.
Ответ (без доказательств): рассмотрим выражение (x_1 + x_2 + ... + x_m)^n. Если раскрыть скобки, получится сумма элементов вида C(n,k_1,...,k_n) * x_1^k_1 * x_2^k_2 * ... * x_m^k^m. Ну к примеру (x_1 + x_2)^2 = C(2, 2, 0) * x_1^2 * x_2^0 + C(2, 1, 1)*x_1^1*x_2^1 + C(2, 0, 2)*x_1^0*x_2^2 = x_1^2 + 2*x_1*x_2 + x_2^2. Суммирование идёт для всех сочетаний k_1, ..., k_m таких, что k_i >= 0, k_1 + ... + k_m = n. C(n,k_1,...,k_n) = n! / (k_1! * k_2! * ... * k_n!). Это в принципе известная формула.
Далее в этой формуле в её правом выражении выбрасываем все слагаемые, у которых k_i = 0. Иными словами вместо k_i >= 0 пишем k_i > 0. Вот эта сумма нам и даст ответ на исходный вопрос.
Как это выражение записать компактней, я не придумал. Ниже код на JavaScript, который вычисляет значение этого выражения "в лоб". Но для больших n будет довольно долго работать.
Итак постановка задачи: у нас генерируется пароль из m групп символов длиной n символов, m <= n. Группы символов не пересекаются, размер каждой группы x_i символов. К примеру пароль длиной 8 из больших, маленьких букв и цифр, m = 3, n = 8, x = [26, 26, 10]. Также дополнительное условие — в пароле должен быть хотя бы один символ из каждой группы.
Вопрос — сколько таких паролей может быть.
Ответ (без доказательств): рассмотрим выражение (x_1 + x_2 + ... + x_m)^n. Если раскрыть скобки, получится сумма элементов вида C(n,k_1,...,k_n) * x_1^k_1 * x_2^k_2 * ... * x_m^k^m. Ну к примеру (x_1 + x_2)^2 = C(2, 2, 0) * x_1^2 * x_2^0 + C(2, 1, 1)*x_1^1*x_2^1 + C(2, 0, 2)*x_1^0*x_2^2 = x_1^2 + 2*x_1*x_2 + x_2^2. Суммирование идёт для всех сочетаний k_1, ..., k_m таких, что k_i >= 0, k_1 + ... + k_m = n. C(n,k_1,...,k_n) = n! / (k_1! * k_2! * ... * k_n!). Это в принципе известная формула.
Далее в этой формуле в её правом выражении выбрасываем все слагаемые, у которых k_i = 0. Иными словами вместо k_i >= 0 пишем k_i > 0. Вот эта сумма нам и даст ответ на исходный вопрос.
Как это выражение записать компактней, я не придумал. Ниже код на JavaScript, который вычисляет значение этого выражения "в лоб". Но для больших n будет довольно долго работать.
function calculateTotalPasswordCount(characterGroups, length) {
const m = characterGroups.length;
const n = length;
const x = [];
for (let i = 1; i <= m; i++) x[i] = BigInt(characterGroups[i - 1].length);
/*
* s =
* \sum_{k_1 + k_2 + \cdots + k_m = n;\ k_1, k_2, ..., k_m > 0 }
* \frac{n!}{k_1! k_2! \cdots k_m!}
* \prod_{i=1}^{m}x_i^{k_i}
*/
const f = []; // f[i] = i!
f[1] = 1n;
for (let i = 2; i <= n; i++) f[i] = f[i - 1] * BigInt(i);
const xp = []; // xp[i][j] = x[i] ** j
for (let i = 1; i <= m; i++) {
xp[i] = [];
xp[i][1] = x[i];
for (let j = 2; j <= n - m + 1; j++) {
xp[i][j] = xp[i][j - 1] * x[i];
}
}
let s = 0n
const k = [];
for (let j = 1; j <= m - 1; j++) k[j] = 1;
k[m] = n - m + 1;
while (true) {
let fk = 1n; // fk = k_1! k_2! ... k_m!
for (let i = 1; i <= m; i++) fk *= f[k[i]];
let sk = 1n; // sk = x_1^{k_1} x_2^{k_2} ... x_m^{k_m}
for (let i = 1; i <= m; i++) sk *= xp[i][k[i]];
s += f[n] / fk * sk;
let j = m - 1;
while (j > 0 && k[m] === 1) {
k[m] += k[j] - 1;
k[j] = 1;
j--;
}
if (j === 0) break;
k[j]++;
k[m]--;
}
return s;
}Re: Забавный факт про сложность пароля
Если вдруг кому будет нужна правильная формула, то описание ниже:
Итак постановка задачи: у нас генерируется пароль из m групп символов длиной n символов, m <= n. Группы символов не пересекаются, размер каждой группы x_i символов. К примеру пароль длиной 8 из больших, маленьких букв и цифр, m = 3, n = 8, x = [26, 26, 10]. Также дополнительное условие — в пароле должен быть хотя бы один символ из каждой группы.
Вопрос — сколько таких паролей может быть.
Ответ (без доказательств): рассмотрим выражение (x_1 + x_2 + ... + x_m)^n. Если раскрыть скобки, получится сумма элементов вида C(n,k_1,...,k_n) * x_1^k_1 * x_2^k_2 * ... * x_m^k^m. Ну к примеру (x_1 + x_2)^2 = C(2, 2, 0) * x_1^2 * x_2^0 + C(2, 1, 1)*x_1^1*x_2^1 + C(2, 0, 2)*x_1^0*x_2^2 = x_1^2 + 2*x_1*x_2 + x_2^2. Суммирование идёт для всех сочетаний k_1, ..., k_m таких, что k_i >= 0, k_1 + ... + k_m = n. C(n,k_1,...,k_n) = n! / (k_1! * k_2! * ... * k_n!). Это в принципе известная формула.
Далее в этой формуле в её правом выражении выбрасываем все слагаемые, у которых k_i = 0. Иными словами вместо k_i >= 0 пишем k_i > 0. Вот эта сумма нам и даст ответ на исходный вопрос.
Как это выражение записать компактней, я не придумал. Ниже код на JavaScript, который вычисляет значение этого выражения "в лоб". Но для больших n будет довольно долго работать.
Итак постановка задачи: у нас генерируется пароль из m групп символов длиной n символов, m <= n. Группы символов не пересекаются, размер каждой группы x_i символов. К примеру пароль длиной 8 из больших, маленьких букв и цифр, m = 3, n = 8, x = [26, 26, 10]. Также дополнительное условие — в пароле должен быть хотя бы один символ из каждой группы.
Вопрос — сколько таких паролей может быть.
Ответ (без доказательств): рассмотрим выражение (x_1 + x_2 + ... + x_m)^n. Если раскрыть скобки, получится сумма элементов вида C(n,k_1,...,k_n) * x_1^k_1 * x_2^k_2 * ... * x_m^k^m. Ну к примеру (x_1 + x_2)^2 = C(2, 2, 0) * x_1^2 * x_2^0 + C(2, 1, 1)*x_1^1*x_2^1 + C(2, 0, 2)*x_1^0*x_2^2 = x_1^2 + 2*x_1*x_2 + x_2^2. Суммирование идёт для всех сочетаний k_1, ..., k_m таких, что k_i >= 0, k_1 + ... + k_m = n. C(n,k_1,...,k_n) = n! / (k_1! * k_2! * ... * k_n!). Это в принципе известная формула.
Далее в этой формуле в её правом выражении выбрасываем все слагаемые, у которых k_i = 0. Иными словами вместо k_i >= 0 пишем k_i > 0. Вот эта сумма нам и даст ответ на исходный вопрос.
Как это выражение записать компактней, я не придумал. Ниже код на JavaScript, который вычисляет значение этого выражения "в лоб". Но для больших n будет довольно долго работать.
function calculateTotalPasswordCount(characterGroups, length) {
const m = characterGroups.length;
const n = length;
const x = [];
for (let i = 1; i <= m; i++) x[i] = BigInt(characterGroups[i - 1].length);
/*
* s =
* \sum_{k_1 + k_2 + \cdots + k_m = n;\ k_1, k_2, ..., k_m > 0 }
* \frac{n!}{k_1! k_2! \cdots k_m!}
* \prod_{i=1}^{m}x_i^{k_i}
*/
const f = []; // f[i] = i!
f[1] = 1n;
for (let i = 2; i <= n; i++) f[i] = f[i - 1] * BigInt(i);
const xp = []; // xp[i][j] = x[i] ** j
for (let i = 1; i <= m; i++) {
xp[i] = [];
xp[i][1] = x[i];
for (let j = 2; j <= n - m + 1; j++) {
xp[i][j] = xp[i][j - 1] * x[i];
}
}
let s = 0n
const k = [];
for (let j = 1; j <= m - 1; j++) k[j] = 1;
k[m] = n - m + 1;
while (true) {
let fk = 1n; // fk = k_1! k_2! ... k_m!
for (let i = 1; i <= m; i++) fk *= f[k[i]];
let sk = 1n; // sk = x_1^{k_1} x_2^{k_2} ... x_m^{k_m}
for (let i = 1; i <= m; i++) sk *= xp[i][k[i]];
s += f[n] / fk * sk;
let j = m - 1;
while (j > 0 && k[m] === 1) {
k[m] += k[j] - 1;
k[j] = 1;
j--;
}
if (j === 0) break;
k[j]++;
k[m]--;
}
return s;
}
calculateTotalPasswordCount(["abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "0123456789"], 12)