请看以下序列:
3,5,6,9,10,12,17,18,20 ......系列中的所有数字都以二进制表示形式设置了2位。你的任务很简单,你必须找到这个序列的第N个数字。
1< = T< = 105
1< = N< = 1014
public class Solution {
public static void main(String[] args) {
/* Enter your code here. Read input from STDIN. Print output to STDOUT. Your class should be named Solution. */
Scanner sc = new Scanner (System.in);
int t = sc.nextInt();
while ( t > 0 ){
int n = sc.nextInt();
t--;
int x =1;
while ( n > 0 ){
int y = 0;
while ( y < x ){
n--;
if ( n == 0 ){
System.out.println((1<<x)|(1<<y));
}
y++;
}
x++;
}
}
}
}
这给我一个超时错误,我可以获得给定输入范围的优化解决方案
答案 0 :(得分:1)
根据我的解释,我将最低有效位的位编号从0编号。因此3位0和1设置。 5有0位和2位等等。
有0个数字,其中最重要的设置位是位0(因为那时没有其他位要设置)。 1位数,位1(3)。两个数字位于第2位(101 = 5和110 = 6)。等等。 m 数字,其中最重要的设置位是位 m 。
这反过来意味着直到并包括位 b 是两个设置位中更重要的数字,有 b *( b < / em> + 1)/ 2个数字。让我们假设这等于 N 。然后根据求解二次方程的形式 b =(sqrt(8 * N + 1) - 1)/ 2.如果这不是一个整数,这是因为 N 并不完全等于我所说的公式。向上找到 b ,然后找到必须为所有内容设置的其他位。
我故意不给你完整的解决方案。你想解决这个问题,你做的工作。我希望我的意见很有用。
另一个 - 更小但更容易 - 优化是:在测试用例中找到最大的 N 。计算最大 N 的序列号,并将它们放入一个数组中(您可以修改问题中的代码来执行此操作)。然后通过在数组中查找它们来打印所有必需的结果。语言挑剔:有人可能会说这不是字面上的优化,因为这个词来自latin optimus ,意思是 best 并且它不会产生最快的计划。
答案 1 :(得分:0)
这是一个整数序列,这意味着我们应该检查The On-Line Encyclopedia of Integer Sequences®。它经常包含相当优化的算法或数学表达式来生成特定整数序列中的元素,因此当您需要优化解决方案时,请查看它。
在searching for 3, 5, 6, 9, 10, 12, 17, 18, 20
之后,我们发现这是OEIS sequence A018900, "Sum of two distinct powers of 2.",其中包括我们应该检查的几个代码段,以确定哪个最快。
检查这些代码段,效率最高的似乎是Hieronymus Fischer的Smalltalk代码(
Version 1
部分中的PROG
):上述代码于2014年4月20日OEIS sequence A018900发布,由Hieronymus Fischer撰写,licensed by The Online Encyclopedia of Integer Sequences在the CC BY-NC 3.0 copyright license下撰写。distinctPowersOf: b "Version 1: Answers the n-th number of the form b^i + b^j, i>j>=0, where n is the receiver. b > 1 (b = 2, for this sequence). Usage: n distinctPowersOf: 2 Answer: a(n)" | n i j | n := self. i := (8*n - 1) sqrtTruncated + 1 // 2. j := n - (i*(i - 1)/2) - 1. ^(b raisedToInteger: i) + (b raisedToInteger: j)
Signed 64-bit long
s run out of space to hold the result and can begin to set incorrect bits after n exceeds 1,953。由于n在实践中不会超过1,014,因此long
结果会没问题。
Signed 32-bit int
s run out of space after n exceeds 465,所以它们不够大。
在这里,我们将Smalltalk算法转换为Java。由于优化效率是您的目标,因此我们会通过使用
<< 3
将小int
值乘以8而>>> 1
乘以int
以将积分除以2来实现加速{1}}:import java.util.Scanner; public class Solution { // Gives the exact floor of the square root of x. // based on Java algorithm by Programmer Olathe // from http://www.codecodex.com/wiki/Calculate_an_integer_square_root#Java public static final int floorSqrt(final int x) { return (int) Math.sqrt(x); }
// Finds the nᵗʰ integer with exactly two bits set. // Cannot properly handle n > 1953. // based on Smalltalk algorithm by Hieronymus Fischer // from https://oeis.org/A018900 public static final long nthWithTwoBitsSet(final int n) { // Find the indexes of the two bits. final int i = (floorSqrt((n << 3) - 1) + 1) >>> 1; final int j = n - ((i*(i - 1)) >>> 1) - 1;
// Return a long with the two bits set. return (1L << i) | (1L << j); }
public static final void main(final String[] args) { final Scanner in = new Scanner(System.in); for (int t = in.nextInt(); t > 0; t--) { System.out.println(nthWithTwoBitsSet(in.nextInt())); } } }
通过将所有三种方法合二为一,我们可以以不良设计为代价获得进一步的效率:
import java.util.Scanner; public class Solution { // Cannot properly handle n > 1953. // based on Java floored-square-root algorithm by Programmer Olathe // from http://www.codecodex.com/wiki/Calculate_an_integer_square_root#Java // based on Smalltalk nᵗʰ-with-two-bits-set algorithm by Hieronymus Fischer // from https://oeis.org/A018900 public static final void main(final String[] args) { final Scanner in = new Scanner(System.in); for (int t = in.nextInt(); t > 0; t--) { final int n = in.nextInt();
// Find the indexes of the two bits. final int i = (((int) Math.sqrt((n << 3) - 1)) + 1) >>> 1; final int j = n - ((i*(i - 1)) >>> 1) - 1;
// Print a long with the two bits set. System.out.println((1L << i) | (1L << j)); } } }