如何减少这个问题的执行时间(即更快的代码) 这个问题来自于CooCheF.[如果有人还在解决这个问题,在尝试之前不要再深入研究这个帖子了。虽然它运行正常,但我需要更快地完成它。我是C、C++的初学者。(我知道数组、字符串和指针的内容,但不知道文件处理等)。.那么,有没有一种方法可以让这个程序运行得更快,而不使它变得复杂(如果它的算法复杂,那就没问题了)。 我也会接受复杂的编码,如果你提到你遵循的是哪本书,在那里给出了它:)。 我目前正在跟踪罗伯特·拉福尔。 节目如下:-
有N个数字a[0],a[1]…a[N-1]。一开始都是0。您必须执行两种类型的操作: 1) 将索引A和B之间的数字增加1。这由命令“0ab”表示 2) 回答指数A和B之间有多少个数字可以被3整除。这由命令“1A B”表示 输入: 第一行包含两个整数,N和Q。接下来的Q行中的每一行都是上述形式的“0ab”或“1ab” 输出: 为表格“1A B”中的每个查询输出1行,其中包含相应查询的所需答案 样本输入:如何减少这个问题的执行时间(即更快的代码) 这个问题来自于CooCheF.[如果有人还在解决这个问题,在尝试之前不要再深入研究这个帖子了。虽然它运行正常,但我需要更快地完成它。我是C、C++的初学者。(我知道数组、字符串和指针的内容,但不知道文件处理等)。.那么,有没有一种方法可以让这个程序运行得更快,而不使它变得复杂(如果它的算法复杂,那就没问题了)。 我也会接受复杂的编码,如果你提到你遵循的是哪本书,在那里给出了它:)。 我目前正在跟踪罗伯特·拉福尔。 节目如下:-,c++,c,time,performance,execution,C++,C,Time,Performance,Execution,有N个数字a[0],a[1]…a[N-1]。一开始都是0。您必须执行两种类型的操作: 1) 将索引A和B之间的数字增加1。这由命令“0ab”表示 2) 回答指数A和B之间有多少个数字可以被3整除。这由命令“1A B”表示 输入: 第一行包含两个整数,N和Q。接下来的Q行中的每一行都是上述形式的“0ab”或“1ab” 输出: 为表格“1A B”中的每个查询输出1行,其中包含相应查询的所需答案 样本输入: 4 7 1 0 3 0 1 2 0 1 3 1 0 0 0 0 3 1 3 3 1 0 3
4 7
1 0 3
0 1 2
0 1 3
1 0 0
0 0 3
1 3 3
1 0 3
样本输出:
4
1
0
2
限制条件:
1 <= N <= 100000
1 <= Q <= 100000
0 <= A <= B <= N - 1
1从我所看到的,您的代码是最优化的
-Alex除了缺少边界检查之外,我认为你的解决方案似乎很好。在检查“c”或开关时,可能会使用“else”,但这将为您节省微不足道的时间。
我不认为你会在任何一本书中找到像这样毫无用处的东西。这在我看来相当有效。
我看到的是,你正在使用,这对于C(AFAIK)是可以的,但是C++中是非法的,对于C++,你需要使用<代码> STD::vector < /COD>或<代码>新< /COD>数组。
我能看到您提高性能的唯一地方是使用部分循环展开,我不建议将其用于玩具样本。您可以做的一个改进是更换
if (c == 0) {
//code here
}
if (c == 1) {
// code here
}
与:
如果您确定c
始终为0或1,您也可以用简单的else
替换else if
真正让你慢下来的是I/O。如果它是一个足够大的列表,它可能需要花费malloc
足够的内存来保存输入和输出。然后在进入循环之前收集所有输入,并在最后显示输出
1) 将索引A和B之间的数字增加1。这由命令“0ab”表示
2) 回答指数A和B之间有多少个数字可以被3整除。这由命令“1A B”表示
最初的数字是0,因此可以被3整除。增加1使这个数字不可整除。下一个增量-数字仍然不可整除。第三个增量使数字再次可整除
我们可以尝试的第一个优化是不让数字增长到2以上:若在增量期间数字从2变为3,则将其设置回零。现在搜索范围成为与0的简单比较。(这样,数组将包含其模3而不是数字。)
第二个优化是使用区段而不是普通数组,例如类似于:将所有具有相同整除性的相邻数字压缩到一个范围内。数组将包含如下结构,而不是数字:
struct extent {
int start; /* 0 .. N-1; extent should have at least one number */
int end; /* 0 .. N */
int n; /* 0, 1, 2; we are only interested in the result of % */
};
最初,数组将包含覆盖所有数字的单个区段{0,N,0}
。在增量步骤中,可能会将一个范围拆分或与相邻的范围合并。这种表示法将加快数字的计数,因为你将不是一个接一个地遍历数组,而是逐块遍历数组。(如果所有范围仅包含一个元素,则仍将降级为线性搜索。)
另一种方法是使用三组索引代替数组。集合#0将包含模3为0、集合#1-1、集合#2-2的所有数字的索引。由于在增量操作期间,我们需要进行搜索,而不是std::set
,因此最好使用每个位标记属于集合的数字的索引
注意,这样我们就根本不保留原始数字。我们隐式地只保留模3的结果
在增量过程中,我们需要找到索引所属的集合,例如集合#n,并将索引移动到下一个(mod 3)集合:在集合n
中将位设置为零,在集合n+1(mod 3)
中将位设置为1。现在,计算可被3整除的数字就像计算集合#0中的非零位一样简单。这可以通过创建一个tempstd::bitset
作为掩码,将范围[a,B]
中的位设置为1,将temp设置为set#0进行掩码,并对生成的位集调用std::bitset::count()
实际上,只能在数组中存储模3的值,而不能存储实际值
增量可以通过一个简单的查找表来完成(以避免比较和分支):
测试3-整除率现在比0-要快得多。这相当复杂,但请听我说。除了“27年的编码经验”之外,我无法列举任何具体的地方
原问题将数字线设置为自然整数0,1,2,3,4,5,6。。。但是,我们只关心可被3整除的数字,因此让我们重新定义数字行,使其仅包含三个值:{2,3,4}并重新映射数字行,以便:
0=>4
1=>2
2=>3
3=>4
4=>2
5=>3
6=>4
.. 等等
你会注意到,在我们的序列中,可被3整除的数字被映射为4。为什么使用{2,3,4}?4在二进制中是100,这意味着任何第3位的元素都可以被3整除。这很容易通过位操作进行测试。if (c == 0) {
//...
} else if (c == 1) {
//...
}
struct extent {
int start; /* 0 .. N-1; extent should have at least one number */
int end; /* 0 .. N */
int n; /* 0, 1, 2; we are only interested in the result of % */
};
char increment[3] = { 1, 2 ,0 };
new_val = increment[old_val];
unsigned char *arr = malloc(n/2 + 1);
// Init all element values to 4:
memset(&arr, 0x44, n/2 + 1);
/**
@param p
p is the address of the byte with the first aligned element to be incremented, &arr[A/2] when A is even, &arr[A/2]+1 when A is odd.
@param j
j is the number of elements to increment. (B-A) when A is even, (B-A-1) when A is odd.
*/
void increment_aligned_block(unsigned char *p, int j)
uint64_t fours;
while (j>16) {
// Find the ones that are value 4
fours = *p & 0x4444444444444444;
// Decrement each that matches by 3
*p -= (fours >> 1 | fours >> 2);
// Add 1 to each of the 16 array elements in the block.
(uint64_t)(*p) += 0x1111111111111111;
p += 8; j -= 16;
}
if (j >= 8) {
// repeat the above for 32-bits (8 elements)
// left as an exercise for the reader.
p += 4; j -= 8;
}
if (j >= 4) {
// repeat the above for 16-bits (4 elements)
// left as an exercise for the reader.
p += 2; j -= 4;
}
if (j >= 2) {
// repeat the above for 8-bits (2 elements)
// left as an exercise for the reader.
p += 1; j -= 2;
}
if (j == 1) {
// repeat the above for 8-bits (1 elements)
// left as an exercise for the reader.
}
}
/**
@param p
p is the address of the byte with the first aligned element to be counted, &arr[A/2] when A is even, &arr[A/2]+1 when A is odd.
@param j
j is the number of elements to count. (B-A) when A is even, (B-A-1) when A is odd.
*/
int count_aligned_block(unsigned char *p, int j)
int count = 0;
uint64_t divisible_map;
while (j > 16) {
// Find the values of 4 in the block
divisible_map = (uint64_t)(*p) & 0x4444444444444444;
// Count the number of 4s in the block,
// 8-bits at a time
while (divisible_map) {
switch (divisible_map & 0x44) {
case 0x04:
case 0x40:
count++;
break;
case 0x44:
count += 2;
break;
default:
break;
}
divisible_map >>= 8;
}
}
// Repeat as above with 32, 16, 8 and 4-bit math.
// Left as an exercise to the reader
return count;
}