【问题标题】:Convert algorithm from o(n) to o(1)将算法从 o(n) 转换为 o(1)
【发布时间】:2015-05-16 09:55:54
【问题描述】:

基本上我想要的是如果一个数字n 可以被b 整除a(count) 次,然后找到a(count),然后将n 除以b 得到a(count) 次。

也就是说,

count = 0;
while(n%b == 0)
    n=n/b;
    count = count + 1;

如何优化这个,让一​​切都可以一步到位

【问题讨论】:

  • 你的意思是n=n/b;
  • 您的意思是查找数字 n 和 m,logₘn 是否是整数?
  • @RealSkeptic 不,给定n=b^a * M,他正在寻找a,而不重视M 的价值。
  • 好的,所以找到logₘn,向下取整,然后除以幂?
  • @RealSkeptic 不,n=62,b=2,正如 AlexeiShestakov 在我也误解了这个问题时所建议的那样。请注意,根据定义,当 n%b != 0 首先出现时,您也可以得到 a=0。

标签: java c++ algorithm optimization division


【解决方案1】:

您可以在O(log(a)) 中通过对已排序“列表”应用二进制搜索来找到等于 1 的最后一个元素。

列表是隐喻的,其中的每个元素都是在通过简单计算查询时即时计算出来的:

list[i] = 1      n % a^i == 0
          0      otherwise

您可以先使用指数找到可能的a 的范围:

curr = b
tempA = 1
while n % curr == 0:
    curr = curr  * curr
    tempA = tempA *2

然后,在[tempA/2, tempA] 范围内运行二进制搜索。这个范围的大小是(a/2),所以找到符号列表1 中的最后一个“元素” - 在O(loga) 乘法中完成。

代码+演示:

private static int specialBinarySearch(int n, int b, int aLow, int aHigh) {
    if (aHigh == aLow) return aHigh;
    int mid = (aHigh - aLow)/2 + aLow;
    //pow method can be optimized to remember pre-calculated values and use them
    int curr = (int)Math.round(Math.pow(b, mid));
    if (n % curr == 0) { //2nd half, or found it:
        if (n % (curr*b) != 0) return mid; //found it
        return specialBinarySearch(n, b, mid+1, aHigh); //2nd half
    }
    else return specialBinarySearch(n, b, aLow, mid); //first half

}

public static int findA(int n, int b) { 
    int curr = b;
    int tempA = 1;
    while (n % curr == 0) {
        curr = curr  * curr;
        tempA = tempA *2;
    }
    return specialBinarySearch(n, b, tempA/2, tempA);
}
public static void main(String args[]) {
    System.out.println(findA(62,2)); //1
    System.out.println(findA(1024,2)); //10
    System.out.println(findA(1,2)); //0
    System.out.println(findA(100,2)); //2
    System.out.println(findA(6804,3)); //5

}

【讨论】:

  • 在这种情况下,我更喜欢蛮力方法,因为它的简单性已经在合理的O(log(n)) 中运行。
  • @Lingxi 我是O(log(a)) = O(log(log(n))
  • 您是否将此代码与原始代码进行了基准测试?我希望它的运行速度比原始代码慢很多(在最坏的情况下,31 个除法和 mods 在一个紧密的循环中)。
  • @Anonymous (1) OP 要求降低大 O 表示的时间复杂度(到 O(1),但我认为这是不可能的),并且代码确实做到了,从 @ 减少了它987654332@ 整数运算到O(loglog(n)) (2) 此代码需要 15 个 mods 并且在 n
  • @Anonymous (4) 尝试对大于int 大小的整数运行它,我相信您会看到改进,因为它将在O(loglog(n)*M(log(n)) 中运行,而在@987654336 中的幼稚方法@ - 其中 M(.) 是除法/乘法/取模的复杂度,是数字中位数的函数。
【解决方案2】:

您无法在 O(1) 中解决此问题,但如果您开始使用以 b 为基数的数字系统,则可以使用另一种方法来解决此问题。

例如,如果我们有一个像 154200 这样的数字,而b 是 10,我们马上就知道答案是 2,因为我们可以简单地计算右侧有多少个零。

同样,在二进制中,如果b 为 2,您只需用二进制表示计算右侧有多少个零。

如果 b 是 5,我们必须使用以 5 为底的奇数表示,其中像 8 这样的数字表示为 13。我们再次知道 a 的答案为零是 n=8b=5,因为右侧没有零。

这不一定会给您带来速度提升,除非可能在 b 是 2 的幂的情况下,您可以使用按位逻辑来推断答案,但它为您提供了一种不同的看待问题的方式用数字代替算术。

【讨论】:

    猜你喜欢
    • 2011-09-10
    • 2021-07-04
    • 2023-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-13
    • 2022-10-13
    相关资源
    最近更新 更多