我必须取两个数字,并找到这两个数字之间的所有数字(包括6或8),但不能都是。因此,输入6 8
将返回2
,而输入60 69
将返回9
(因为68不计算在内)。
这是一件非常简单的事情,但我必须在不到一秒的时间内完成,即使是在千万亿的数字上。这是不可能迭代完成的事实,这让我想知道是否有一些基本的技巧可以跳过检查我错过的99%的数字。我能优化的最多的是能够进行10^ [two less than the number of digits in the difference between the two numbers]
的跳跃,让我们将指数定义为d
。我让我程序在数字有数字6和8,或者没有这两个数字时进行这些跳转,并以至少d
个零结束。如果数字没有数字6和8,在1和10之间的具有6或8的数字的数量的值被添加到特殊数字的数量(被添加的该第一值已经被保存在存储器中的数组中)。
在任何情况下,我认为展示我尝试的解决方案的最佳方式是展示我的代码。不过,如果您对改进我的问题有任何建议,请随时提出。
#include <iostream>
int main() {
int_fast64_t low; //starting number
int_fast64_t high; //ending number
std::cin >> low >> high; std::cin.ignore();
/*Starts out holding the difference between high and low,
then will hold two less than the number of digits in that difference*/
int_fast64_t lowHighDifference = high - low;
//10 to various powers used to not have to iterate over every number between low & high
static int_fast64_t pow10[] = {0, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
//The number of numbers with 6 xor 8 between 1 and 10^index
static int_fast64_t numspecial[] = {0, 2, 34, 434, 4930, 52562, 538594, 5371634, 52539010, 506405522};
//Find how large the jumps one can make are based on the difference between high and low
for (int_fast8_t iterator = 9; iterator > 0; iterator--) {
if (lowHighDifference > pow10[iterator]) {
/*This needs to have two fewer digits than the difference between low & high,
otherwise it risks not being used*/
lowHighDifference = iterator - 1;
iterator = 0;
}
}
/*Incremented by one every time a number with 6 xor 8 is ecountered,
and by numspecial[lowHighDifference] every time a number without 6 or 8,
and at least as many zeros at the end as pow10[lowHighDifference] is encountered*/
int_fast64_t specialNumbers = 0;
for (int_fast64_t iterator = low; iterator <= high; iterator++) {
char stringNum[21]; //long ints can't have more than 20 digits
/*Copy iterator into a string to check each digit individually,
while recording the length that string ends up being. */
int_fast8_t stringNumLength = sprintf(stringNum, "%ld", iterator),
/*Starts at 0, will turn to 6 or 8 with the first of those found,
then will be set to -1 if the other number is found */
sixOrEight = 0;
for (int_fast8_t stringNumIterator = 0; stringNumIterator < stringNumLength; stringNumIterator++) {
if (stringNum[stringNumIterator] == '8') {
if (sixOrEight == 6) {
sixOrEight = -1;
stringNumIterator = stringNumLength;
}
else
sixOrEight = 8;
}
else if (stringNum[stringNumIterator] == '6') {
if (sixOrEight == 8) {
sixOrEight = -1;
stringNumIterator = stringNumLength;
}
else
sixOrEight = 6;
}
}
/*The basic case in which a six or an eight was found. I don't know how I can optimize this,
but it's the main part slowing this program down. */
if (sixOrEight > 0) {
specialNumbers++;
}
else if (lowHighDifference > 0) {
//If neither six or eight were found
if (sixOrEight == 0) {
if (iterator % pow10[lowHighDifference] == 0 && pow10[lowHighDifference] < (high - iterator)) {
specialNumbers += numspecial[lowHighDifference];
iterator += pow10[lowHighDifference] - 1;
}
} //If sixOrEight is < 0 and iterator has at least as many zeros at the end as pow10[lowHighDifference]
else if (iterator % pow10[lowHighDifference] == 0) {
//Add 10^lowHighDifference to iterator without adding to specialNumbers
iterator += pow10[lowHighDifference] - 1;
}
}
}
std::cout << specialNumbers;
return 0;
}
字符串
1条答案
按热度按时间nvbavucw1#
完整的解决方案在本文底部
我花了几个小时来做这个,但我认为我有最佳的解决方案。谢谢你提供这个挑战。数字计算问题是我的一个爱好(错误)。
酒店地理位置
首先,我为这个问题发明了一些术语:
NQ或“非限定”。NQ数是指在其数字集合中既没有
6
也没有8
的数。Q6或“Qualified by 6”是任何在其数字集合中有
6
的数字,但没有8。Q8或“Qualified by 8”是任何在其数字集合中有
8
的数字,但没有六。PNQ或“永久不合格”。这是一个包含6或8的数字。
任何NQ数都可以通过简单地在其末尾附加
6
或8
来变得合格。(例如,123
是NQ,但1236
是“合格”)我之所以使用这个“限定”术语,是因为解决方案需要考虑将数字构建为从左到右的数字串。例如,一个非限定数,如
123
,可以简单地通过在其上附加6
来变得限定。通过在123
上附加6
,该数字变为1236
,并且是Q6。任何合格的数字都可以通过附加一个
6
或8
使其具有两个数字而永久不合格。例如,654
是Q6,但如果我们在它上面附加一个8
,它将永久不合格。任何附加在这个数字上的数字仍然会导致这个数字是PNQ。慢解
所以让我们来介绍第一批代码。我上面讨论的正式枚举:
字符串
让我们介绍一个帮助函数,它可以接受任何64位数字并告诉我们它是什么类型:
型
在这一点上,我们可以很容易地构建一个暴力解决方案来计算给定范围内Q6或Q8数字的计数。
型
但是一旦我们开始进入一个有数十亿个数字的范围,那就不成比例了。
号码模式
我注意到了一些关于数字的事情。
取除零以外的所有个位数:
取10到99之间的所有两位数。如果你粗略地浏览一下这些数字,你会发现下面的统计数据是正确的:
要计算所有3位数的统计数据,你不必进行暴力搜索。你会意识到以下几点:
因此,下面是一个通用的公式,用于计算任何一组N位数中有多少种数字:
现在,如果问题空间是“计算N位数的计数与6或8”,那么递归函数将是容易的。但你如何计算范围内的数字在不同的数字边界?
分解示例
假设你想找到
125
和455
之间的合格数字范围。首先,通过截断数字,从
125-455
开始递归地分解问题。首先求解
12-45
,这需要在此之前求解1-4
。然后应用公式向上计数到下一个数字范围。但您必须添加一个修复步骤来取消计数不在范围内的数字(120-124和456-459)。型
最终解决方案
型