以下sum
的简单实现如何更快?
private long sum( int [] a, int begin, int end ) {
if( a == null ) {
return 0;
}
long r = 0;
for( int i = begin ; i < end ; i++ ) {
r+= a[i];
}
return r;
}
编辑
背景是有序的。
阅读关于编码恐怖的最新条目,我来到这个网站:http://codility.com这个有趣的编程测试。
无论如何,我在提交中得到60分中的60分,基本上(我认为)是因为这个总和的实现,因为我失败的那些部分是性能部分。我得到了TIME_OUT_ERROR的
所以,我想知道算法中的优化是否可行。
因此,不允许内置函数或程序集。我可以用C,C ++,C#,Java或其他任何方式完成。
编辑
像往常一样,mmyers是对的。我确实对代码进行了分析,我看到大部分时间花在了这个函数上,但我不明白为什么。所以我所做的就是抛弃我的实现并从一个新实现开始。这次我得到了一个最佳解决方案[根据San Jacinto O(n) - 请参阅下面的MSN评论 - ]
这次我对Codility有81%的认可,我认为这已经足够了。问题是我没有花30分钟。但大约2小时。但我想这让我仍然是一个优秀的程序员,因为我可以解决这个问题,直到找到最佳解决方案:
这是我的结果。
my result on codility http://img534.imageshack.us/img534/6804/codility.png
我从不明白那些“......的组合”是什么,也不知道如何测试“extreme_first”
答案 0 :(得分:6)
我认为你的问题不在于对数组进行求和的函数,可能是你经常将数组WAY求和。如果你简单地将WHOLE数组求和一次,然后逐步遍历数组直到找到第一个均衡点,那么你应该充分减少执行时间。
int equi ( int[] A ) {
int equi = -1;
long lower = 0;
long upper = 0;
foreach (int i in A)
upper += i;
for (int i = 0; i < A.Length; i++)
{
upper -= A[i];
if (upper == lower)
{
equi = i;
break;
}
else
lower += A[i];
}
return equi;
}
答案 1 :(得分:6)
这是我的解决方案,我的得分为100%
{{1}}
答案 2 :(得分:5)
这段代码很简单,除非a
相当小,否则它可能主要受内存带宽的限制。因此,你可能不希望通过处理求和部分本身来获得任何显着的收益(例如,展开循环,倒数而不是向上,并行执行求和 - 除非它们在单独的CPU上,每个都有它的自己访问内存)。最大的收益可能来自发布一些预加载指令,因此大多数数据在您需要时已经在缓存中。剩下的就是(充其量)让CPU快点起来,所以等待的时间会更长。
编辑:看来上面的大部分内容与真正的问题没什么关系。它有点小,所以可能很难阅读,但是,我尝试使用std::accumulate()
进行初始添加,它似乎认为没有问题:
答案 3 :(得分:5)
如果这是基于实际的样本问题,那么您的问题不是总和。您的问题是如何计算均衡指数。天真的实现是O(n ^ 2)。最佳解决方案要好得多。
答案 4 :(得分:3)
我不相信问题出在您提供的代码中,但不知何故,更大的解决方案必须是次优的。这段代码看起来很适合计算一个数组的总和,但也许并不是解决整个问题所需要的。
答案 5 :(得分:3)
一些提示:
使用分析器确定您花费大量时间的位置。
编写良好的性能测试,以便您可以确定所做的每个更改的确切效果。记下小心笔记。
如果事实证明瓶颈是确保您在阵列中取消引用合法地址的检查,并且您可以保证开始和结束实际上都在阵列内,那么考虑修复数组,指向数组,并在指针而不是数组中执行算法。指针不安全;他们不会花任何时间检查以确保你仍然在阵列中,所以他们可以更快一些。但是你负责确保你不会破坏地址空间中每个字节的内存。
答案 6 :(得分:2)
你可能获得的最快速度是将int数组16字节对齐,将32字节流式传输到两个__m128i
变量(VC ++)并调用_mm_add_epi32
(再次,VC ++内部)大块的。重新使用其中一个块继续添加到它中,并在最后一个块上提取你的四个整数并以旧式方式添加它们。
更大的问题是为什么简单的加法是值得优化的候选者。
编辑:我认为这主要是学术活动。也许我明天会试一试并发布一些结果......
答案 7 :(得分:1)
在C#3.0中,我的计算机和我的操作系统这个速度更快,只要你能保证4个连续数字不会溢出int的范围,可能因为大多数添加都是使用32位数学运算完成的。 但是,使用更好的算法通常可以提供比任何微优化更快的速度。
100毫秒元素阵列的时间:
4999912596452418 - &gt; 233毫秒(总和)
4999912596452418 - &gt; 126ms(sum2)
private static long sum2(int[] a, int begin, int end)
{
if (a == null) { return 0; }
long r = 0;
int i = begin;
for (; i < end - 3; i+=4)
{
//int t = ;
r += a[i] + a[i + 1] + a[i + 2] + a[i + 3];
}
for (; i < end; i++) { r += a[i]; }
return r;
}
答案 8 :(得分:1)
我做了同样的天真实现,这是我的O(n)解决方案。我没有使用IEnumerable Sum方法,因为它在Codility上不可用。我的解决方案仍然没有检查溢出,如果输入有大数字,所以它没有在Codility上的特定测试失败。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
var list = new[] {-7, 1, 5, 2, -4, 3, 0};
Console.WriteLine(equi(list));
Console.ReadLine();
}
static int equi(int[] A)
{
if (A == null || A.Length == 0)
return -1;
if (A.Length == 1)
return 0;
var upperBoundSum = GetTotal(A);
var lowerBoundSum = 0;
for (var i = 0; i < A.Length; i++)
{
lowerBoundSum += (i - 1) >= 0 ? A[i - 1] : 0;
upperBoundSum -= A[i];
if (lowerBoundSum == upperBoundSum)
return i;
}
return -1;
}
private static int GetTotal(int[] ints)
{
var sum = 0;
for(var i=0; i < ints.Length; i++)
sum += ints[i];
return sum;
}
}
}
答案 9 :(得分:1)
这是一个想法:
private static ArrayList equi(int[] A)
{
ArrayList answer = new ArrayList();
//if(A == null) return -1;
if ((answer.Count == null))
{
answer.Add(-1);
return answer;
}
long sum0 = 0, sum1 = 0;
for (int i = 0; i < A.Length; i++) sum0 += A[i];
for (int i = 0; i < A.Length; i++)
{
sum0 -= A[i];
if (i > 0) { sum1 += A[i - 1]; }
if (sum1 == sum0) answer.Add(i);
//return i;
}
//return -1;
return answer;
}
答案 10 :(得分:0)
这对O(n ^ 2)算法没有帮助,但你可以优化你的总和。
在之前的公司,我们让英特尔过来并给我们优化提示。他们有一个非显而易见且有点酷的技巧。替换:
long r = 0;
for( int i = begin ; i < end ; i++ ) {
r+= a[i];
}
与
long r1 = 0, r2 = 0, r3 = 0, r4 = 0;
for( int i = begin ; i < end ; i+=4 ) {
r1+= a[i];
r2+= a[i + 1];
r3+= a[i + 2];
r4+= a[i + 3];
}
long r = r1 + r2 + r3 + r4;
// Note: need to be clever if array isn't divisible by 4
为什么这会更快: 在最初的实现中,您的变量r是一个瓶颈。每次循环时,你必须从存储器阵列a中拉出数据(这需要几个周期),但你不能并行执行多次拉取,因为循环的下一次迭代中r的值取决于值在循环的这个迭代中的r。在第二个版本中,r1,r2,r3和r4是独立的,因此处理器可以超线程执行它们。只有在最后才会聚集在一起。
答案 11 :(得分:0)
以下是我的回答以及如何解决这个问题的解释。它会让你100%
class Solution
{
public int solution(int[] A)
{
long sumLeft = 0; //Variable to hold sum of elements to the left of the current index
long sumRight = 0; //Variable to hold sum of elements to the right of the current index
long sum = 0; //Variable to hold sum of all elements in the array
long leftHolder = 0; //Variable that holds the sum of all elements to the left of the current index, including the element accessed by the current index
//Calculate the total sum of all elements in the array and store it in the sum variable
for (int i = 0; i < A.Length; i++)
{
//sum = A.Sum();
sum += A[i];
}
for (int i = 0; i < A.Length; i++)
{
//Calculate the sum of all elements before the current element plus the current element
leftHolder += A[i];
//Get the sum of all elements to the right of the current element
sumRight = sum - leftHolder;
//Get the sum of all elements of elements to the left of the current element.We don't include the current element in this sum
sumLeft = sum - sumRight - A[i];
//if the sum of the left elements is equal to the sum of the right elements. Return the index of the current element
if (sumLeft == sumRight)
return i;
}
//Otherwise return -1
return -1;
}
}
答案 12 :(得分:0)
这让我100%使用Javascript:
function solution(A) {
if (!(A) || !(Array.isArray(A)) || A.length < 1) {
return -1;
}
if (A.length === 1) {
return 0;
}
var sum = A.reduce(function (a, b) { return a + b; }),
lower = 0,
i,
val;
for (i = 0; i < A.length; i++) {
val = A[i];
if (((sum - lower) - val) === (lower)) {
return i;
}
lower += val;
}
return -1;
}
答案 13 :(得分:0)
测试此代码的100%正确性和性能
Private Function equi(ByVal A() As Integer) As Integer
Dim index As Integer = -1
If A.Length > 0 And Not IsDBNull(A) Then
Dim sumLeft As Long = 0
Dim sumRight As Long = ArraySum(A)
For i As Integer = 0 To A.Length - 1
Dim val As Integer = A(i)
sumRight -= val
If sumLeft = sumRight Then
index = i
End If
sumLeft += val
Next
End If
Return index
End Function
答案 14 :(得分:0)
100%O(n)C溶液
int equi ( int A[], int n ) {
long long sumLeft = 0;
long long sumRight = 0;
int i;
if (n <= 0) return -1;
for (i = 1; i < n; i++)
sumRight += A[i];
i = 0;
do {
if (sumLeft == sumRight)
return i;
sumLeft += A[i];
if ((i+1) < n)
sumRight -= A[i+1];
i++;
} while (i < n);
return -1;
}
可能不完美,但无论如何它都会通过测试:)
不能说我是Codility的忠实粉丝 - 这是一个有趣的想法,但我发现这些要求有点过于模糊。我想如果他们给你的要求+一套测试这些要求的单元测试并且然后要求你编写代码,我会更感动。无论如何,这就是大多数TDD的发生方式。我不认为盲目地做任何其他事情,除了允许他们抛出一些角落的情况。
答案 15 :(得分:0)
private static int equi ( int[] A ) {
if (A == null || A.length == 0)
return -1;
long tot = 0;
int len = A.length;
for(int i=0;i<len;i++)
tot += A[i];
if(tot == 0)
return (len-1);
long partTot = 0;
for(int i=0;i<len-1;i++)
{
partTot += A[i];
if(partTot*2+A[i+1] == tot)
return i+1;
}
return -1;
}
我认为阵列是双性的,所以如果存在均衡指数,那么一半的权重在左边。所以我只将partTot(部分总数)x 2与数组的总重量进行比较。 Alg取O(n)+ O(n)
答案 16 :(得分:0)
{In Pascal + Assembly}
{$ASMMODE INTEL}
function equi (A : Array of longint; n : longint ) : longint;
var c:Longint;
label noOverflow1;
label noOverflow2;
label ciclo;
label fine;
label Over;
label tot;
Begin
Asm
DEC n
JS over
XOR ECX, ECX {Somma1}
XOR EDI, EDI {Somma2}
XOR EAX, EAX
MOV c, EDI
MOV ESI, n
tot:
MOV EDX, A
MOV EDX, [EDX+ESI*4]
PUSH EDX
ADD ECX, EDX
JNO nooverflow1
ADD c, ECX
nooverflow1:
DEC ESI
JNS tot;
SUB ECX, c
SUB EDI, c
ciclo:
POP EDX
SUB ECX, EDX
CMP ECX, EDI
JE fine
ADD EDI, EDX
JNO nooverflow2
DEC EDI
nooverflow2:
CMP EAX, n
JA over
INC EAX
JMP ciclo
over:
MOV EAX, -1
fine:
end;
End;
答案 17 :(得分:0)
在C ++中,以下内容:
int* a1 = a + begin;
for( int i = end - begin - 1; i >= 0 ; i-- )
{
r+= a1[i];
}
可能会更快。 优点是我们在循环中与零进行比较。
当然,使用真正良好的优化器,应该没有任何区别。
另一种可能性是
int* a2 = a + end - 1;
for( int i = -(end - begin - 1); i <= 0 ; i++ )
{
r+= a2[i];
}
这里我们以相同的顺序遍历项目,而不是与end
进行比较。
答案 18 :(得分:0)
只是一些想法,不确定是否直接访问指针
int* pStart = a + begin;
int* pEnd = a + end;
while (pStart != pEnd)
{
r += *pStart++;
}
答案 19 :(得分:0)
如果您正在使用C或C ++并开发现代桌面系统,并且愿意学习一些汇编程序或了解GCC内在函数,那么可以使用SIMD instructions。
This library是float
和double
数组可能的示例,因为SSE也有整数指令,因此整数算术应该可以得到类似的结果。
答案 20 :(得分:-1)
我为这个得分100%:
int equi (int[] A)
{
if (A == null) return -1;
long sum0 = 0, sum1 = 0;
for (int i = 0; i < A.Length; i++) sum0 += A[i];
for (int i = 0; i < A.Length; i++)
{
sum0 -= A[i];
if (i > 0)
{
sum1 += A[i - 1];
}
if (sum1 == sum0) return i;
}
return -1;
}
答案 21 :(得分:-1)
这可能很旧,但这里是Golang的解决方案,合格率为100%:
package solution
func Solution(A []int) int {
// write your code in Go 1.4
var left int64
var right int64
var equi int
equi = -1
if len(A) == 0 {
return equi
}
left = 0
for _, el := range A {
right += int64(el)
}
for i, el := range A {
right -= int64(el)
if left == right {
equi = i
}
left += int64(el)
}
return equi
}