查找数字在给定范围内最高的数字

时间:2020-08-07 13:26:27

标签: c# algorithm math

对于给定的输入n,任务是找到largest integer that is <= n and has the highest digit sum

例如:

solve(100) = 99. Digit Sum for 99 = 9 + 9 = 18. No other number <= 100 has a higher digit sum.
solve(10) = 9
solve(48) = 48. Note that 39 is also an option, but 48 is larger.

Input range is 0 < n < 1e11

我尝试了什么?

我尝试了2种方法。首先,我尝试使用以下数学运算获取每个数字:

public static long solve(long n)
{
    var answer = 0;
    var highestSum = 0;

    for (var i = 1; i <= n; i++)
    {
        var temp = i;
        var sum = 0;
        while (temp > 0)
        {
            sum += temp % 10;
            temp /= 10;
        }
        if (sum >= highestSum)
        {
            highestSum = sum;
            answer = i;
        }
    }

    return answer;
}

第二次尝试,我尝试使用Linq扩展,如下所示:

public static long solve(long n)
{
    var answer = 0;
    var highestSum = 0;

    for (var i = 1; i <= n; i++)
    {
        var sum = i.ToString().Sum(x => x - '0');
        if (sum >= highestSum)
        {
            highestSum = sum;
            answer = i;
        }
    }

    return answer;
}

我的两个解决方案似乎都返回正确的值并为较小的值工作,但是对于较大的输入,它们似乎需要很长时间才能执行。如何使其更快地通过数字运行?是否有针对此任务的特定算法,还是我做错了其他事情?

2 个答案:

答案 0 :(得分:4)

我们可以达到这个O(n中的位数)

如果我们迭代减少一个数字并将其右边的所有其他数字更改为9,则可以实现此目标。

n是我们当前的电话号码。

我们可以使用以下内容找到下一个号码:

b是10的幂,代表当前数字的位置。每次迭代后,我们将n减少为n/10,并将b更改为b*10

我们使用(n – 1) * b + (b – 1);

例如,如果数字为n = 521b = 1,则

(521 – 1) * 1 + (1-1)为您提供520,这是我们需要做的事情,将位置编号减少1,并用9替换右边的所有其他数字

n /= 10给您n作为52之后,b*=10给您b作为10,又被执行为{{1 }},这就是我们要做的(52-1)*(10) + 9,将当前索引减少519,将所有其他权限增加1

9

答案 1 :(得分:1)

可接受的答案很棒,但是我在寻找一种方法来确定正确答案而又不对数字进行求和并相互比较的过程中呆滞了。

我尝试了一些操作(如您所见,是否可以查看编辑历史记录),但是找不到公式。无奈之下,我编写了一个实用程序,向我显示了从19999999的所有数字,这些数字没有较小的数字和较大的数字,可以通过查看不足够大的数字来查看缺少的模式规模。

令我有些惊讶的是,前253个数字中只有10个数字与较小的数字相比,金额最大!我以某种方式认为这个数字会更大。

另外,事实证明,有一个显而易见的模式出现得相当快,并且在1000万次迭代中保持不变,所以我认为这是一个很好的模式。

下面是一些连续输出块的小样本:

0,1,2,3,4,5,6,7,8,9,
18,19,28,29,38,39,48,49,
58,59,68,69,78,79,88,89,98,99,189,198

8899,8989,8998,8999,
9899,9989,9998,9999,
18999,19899,19989,19998,19999

98999,99899,99989,99998,99999,
189999,198999,199899,199989,199998,199999

7899999,7989999,7998999,7999899,7999989,7999998,7999999,
8899999,8989999,8998999,8999899,8999989,8999998,8999999,
9899999,9989999,9998999,9999899,9999989,9999998,9999999

很明显!

  • 如果数字为一位,则为最高数字。
  • 如果除第一个数字之外的所有数字都是全9或全9且带有单个8,则总和最高。
  • 否则,最高的数字是第一位数字比原始数字小一位,然后是所有9

这是一个代码实现:

public static long Solve(long n)
{
    if (HasValidSuffix(n)) return n;

    long firstDigit;
    int numDigits;

    // Loop to determine the first digit and number of digits in the input
    for (firstDigit = n, numDigits = 1; firstDigit > 9; firstDigit /= 10, numDigits++) ;

    return Enumerable.Range(0, numDigits - 1)
        .Aggregate(firstDigit - 1, (accumulator, next) => accumulator * 10 + 9);
}

// Returns true for positive numbers less than 10 or 
// numbers that end in either all 9's or all 9's and one 8
public static bool HasValidSuffix(long input)
{
    var foundAnEight = false;

    for (var n = input; n > 9; n /= 10)
    {
        var lastDigit = n % 10;
        if (lastDigit < 8) return false;
        if (lastDigit == 9) continue;
        if (foundAnEight) return false;
        foundAnEight = true;
    }

    return true;
}