/// <summary> /// Метод поиска решения задачи перебором. Используется для тестирования разрабатываемого метода. /// Да, тесты надо писать по-другому. /// </summary> /// <param name="usingDigits">Используемые цифры в системе счисления.</param> /// <param name="digitsCount">Количество цифр в проверяемых числах.</param> /// <param name="checkingRanks">Количество проверяемых 'разрядов' (цифр) слева и справа.</param> /// <param name="useLog">Использовать ли вывод в консоль для логов.</param> /// <returns>Результат решения задачи.</returns> private static BigInteger TestMultiThreadGetLuckyTicketsCount(IReadOnlyList <char> usingDigits, int digitsCount, byte checkingRanks, bool useLog) { if (useLog) { Console.WriteLine($"Тестируем многопоточным перебором с параметрами ({usingDigits.Count}, {digitsCount}, {checkingRanks})..."); } var minNumberIncl = 0L; var maxNumberNotIncl = new NumberX(new string(usingDigits[usingDigits.Count - 1], digitsCount), usingDigits).ToInteger() + 1; if (maxNumberNotIncl < 0) { throw new Exception(); } var luckyTicketCount = new BigInteger(0); var resultLocker = new object(); var task = Parallel.For(minNumberIncl, maxNumberNotIncl, base10Number => { var baseXNumber = new NumberX(base10Number, usingDigits); var delta = 0; for (var rank = 0; rank < checkingRanks; rank++) { delta += baseXNumber.ToInteger(rank) - baseXNumber.ToInteger(digitsCount - 1 - rank); } if (delta == 0) { lock (resultLocker) { luckyTicketCount++; } } }); if (useLog) { Console.WriteLine($"[Тестовый многопоточный перебор] Результат: {luckyTicketCount}"); } return(luckyTicketCount); }
/// <summary> /// Метод поиска решения задачи перебором. Используется для тестирования разрабатываемого метода. /// Да, тесты надо писать по-другому. /// </summary> /// <param name="usingDigits">Используемые цифры в системе счисления.</param> /// <param name="digitsCount">Количество цифр в проверяемых числах.</param> /// <param name="checkingRanks">Количество проверяемых 'разрядов' (цифр) слева и справа.</param> /// <param name="useLog">Использовать ли вывод в консоль для логов.</param> /// <returns>Результат решения задачи.</returns> private static BigInteger TestSingleThreadGetLuckyTicketsCount(IReadOnlyList <char> usingDigits, int digitsCount, byte checkingRanks, bool useLog) { if (useLog) { Console.WriteLine($"Тестируем однопоточным перебором с параметрами ({usingDigits.Count}, {digitsCount}, {checkingRanks})..."); } var luckyTicketCount = new BigInteger(0); var numberX = new NumberX(0, usingDigits); var maxNumberX = new NumberX(new string(usingDigits[usingDigits.Count - 1], digitsCount), usingDigits); maxNumberX.IncrementAbs(); while (numberX != maxNumberX) { var delta = 0; for (var rank = 0; rank < checkingRanks; rank++) { delta += numberX.ToInteger(rank) - numberX.ToInteger(digitsCount - 1 - rank); } if (delta == 0) { luckyTicketCount++; } numberX.IncrementAbs(); } if (useLog) { Console.WriteLine($"[Тестовый однопоточный перебор] Результат: {luckyTicketCount}"); } return(luckyTicketCount); }
/// <summary> /// Метод решения задачи. Находит количество красивых чисел в заданной системе счисления, у которых сумма первых [checkingRanks] цифр слева равна сумме первых [checkingRanks] цифр справа. /// </summary> /// <param name="usingDigits">Цифры, используемые в системе счисления.</param> /// <param name="digitsCount">Количество цифр в проверяемых числах.</param> /// <param name="checkingRanks">Количество проверяемых 'разрядов' (цифр) слева и справа.</param> /// <param name="useLog">Использовать ли вывод в консоль для логов.</param> /// <returns>Результат решения задачи.</returns> private static BigInteger GetLuckyTicketsCount(IReadOnlyList <char> usingDigits, byte digitsCount, byte checkingRanks, bool useLog) { var numberBase = usingDigits.Count; if (useLog) { Console.WriteLine(); Console.WriteLine("========================================"); Console.WriteLine(); Console.WriteLine($"Начинаем решать задачу поиска красивых чисел для {digitsCount}-значных чисел в {numberBase}-ичной системе счисления с проверкой по {checkingRanks} первых цифр слева и справа:"); } var notCheckingRanks = digitsCount - checkingRanks * 2; if (notCheckingRanks < 0) { Console.WriteLine($"Ошибка: Аргумент '{nameof(digitsCount)}' должен быть минимум в 2 раза больше, чем аргумент '{nameof(checkingRanks)}'."); return(-1); } var notCheckingMultiplier = 1; if (notCheckingRanks == 0) { if (useLog) { Console.WriteLine("Обнаружено, что все цифры в числах будут проверяться. Дополнительная оптимизация неактуальна."); } } else { notCheckingMultiplier = (int)Math.Pow(numberBase, notCheckingRanks); if (useLog) { Console.WriteLine($"Обнаружено, что {notCheckingRanks} цифр(ы) в числах не будут проверяться. Следовательно, на каждую искомую проверяемую комбинацию у нас будет по {numberBase}^{notCheckingRanks} подходящих вариантов."); } } if (useLog) { Console.WriteLine($"Т.к. по заданию у нас все числа содержат {digitsCount} цифр (включая ведущие нули) и мы рассматриваем все без исключения такие числа, для каждой суммы первых {checkingRanks} цифр 'слева' гарантированно будет один или несколько подходящих вариантов расположения первых {checkingRanks} цифр 'справа' с такой же суммой цифр."); } var numberX = new NumberX(0, usingDigits); var maxNumberX = new NumberX(new string(usingDigits[numberBase - 1], checkingRanks), usingDigits); var differentSumsCount = maxNumberX.GetDigitsSum() + 1; // не забываем про ноль. if (useLog) { Console.WriteLine($"Получается, что для решения задачи достаточно перебрать {numberBase}-ичные числа от '{numberX}' до '{maxNumberX}', сосчитать количество вариантов N для каждой суммы цифр 'слева' - для каждого из этих вариантов будет N вариантов искомых цифр 'справа', т.е. чтобы получить результирующее количество вариантов - нужно полученное значение возвести в квадрат."); Console.WriteLine($"Определили, что различных сумм цифр у нас будет {differentSumsCount}."); } var sums = new int[differentSumsCount]; maxNumberX.IncrementAbs(); // чтобы последняя проверка с самим макс. числом тоже прошла while (numberX.ToString() != maxNumberX.ToString()) { var digitsSum = numberX.GetDigitsSum(); sums[digitsSum]++; numberX.IncrementAbs(); } var result = new BigInteger(0); if (useLog) { Console.WriteLine("Сосчитали количество сочетаний цифр для каждой из сумм цифр:"); } for (var sum = 0; sum < differentSumsCount; sum++) { var variantsCount = sums[sum]; result += new BigInteger(variantsCount) * new BigInteger(variantsCount); if (useLog) { Console.WriteLine($"- {sum}: {variantsCount}"); } } if (useLog) { Console.WriteLine($"Итоговое количество искомых проверяемых комбинаций: {result}"); } if (notCheckingMultiplier != 1) { result *= new BigInteger(notCheckingMultiplier); if (useLog) { Console.WriteLine($"Множитель дополнительной оптимизации, описанной выше: {notCheckingMultiplier}."); } } if (useLog) { Console.WriteLine("----------------------------------------"); Console.WriteLine($"Результат выполнения задачи: {result}"); Console.WriteLine("----------------------------------------"); Console.WriteLine(); Console.WriteLine("========================================"); Console.WriteLine(); } return(result); }