在N * N矩阵中找到总数据

时间:2017-12-05 11:57:17

标签: python algorithm

矩阵由N×N个块组成。块号等于行号和列号的总和。每个块由数据组成,并且数据等于块号的偶数和奇数之和的差。计算n * n块的总数据

i / o格式

lets n = 4
so
matrix will be
2 3 4 5
3 4 5 6
4 5 6 7
5 6 7 8

所以总数据= 2 + 3 + 4 + 5 + 3 + 4 + 5 + 6 + 4 + 5 + 6 + 7 + 5 + 6 + 7 + 8 = 80

如果在任何情况下块的数量是4256,那么其中的数据将是abs(diff(sum(偶数) - sum(奇数位))),这是abs((4 + 2 + 6) - (5) )= 7

我天真的尝试

n = int(raw_input())
sum1=0
sum2=0
for i in range(1,n+1):
    for j in range(1,n+1):
        sum1 = i+j
        diffsum = diff(sum1)
        sum2 = sum2+diffsum
print sum2

再次优化尝试

def diff(sum1):
    sum1 = str(sum1)
    m = sum([int(i) for i in sum1 if int(i) % 2 == 0])
    f = sum([int(i) for i in sum1 if int(i) % 2 != 0])
    return abs(m - f)


n = int(raw_input())
sum1 = 0
k = 1
# t1 = time.time()
p = 2 * n
for i in range(2, n + 2):
    diffsum = diff(i)
    diffsum1 = diff(p)
    sum1 = sum1 + (diffsum * k)
    sum1 = sum1 + (diffsum1 * k)
    p = p - 1
    k = k + 1
sum1 = sum1 - (diff(n + 1) * n)
print sum1

diff在两种情况下都是常见的功能。我需要使用以下算法进行更多优化

1 个答案:

答案 0 :(得分:1)

您的优化方法仅为每个数字计算一次数字和,因此乍一看,没有任何内容可以从记忆中获得。

您可以通过将两个循环合并为一个来改善diff函数的性能,并使用字典查找是否添加或减去数字:

value = dict(zip("0123456789", (0, -1, 2, -3, 4,-5, 6,-7, 8,-9)))

def diff2(s):
    s = str(s)
    return abs(sum([value[i] for i in s]))

这需要转换为字符串。通过手动计算数字可以更快(但不多):

dvalue = [0, -1, 2, -3, 4,-5, 6,-7, 8,-9]

def diff(s):
    t = 0

    while s:
        t += dvalue[s % 10]
        s //= 10

    return abs(t)

最后,您可以利用以下事实:按顺序计算从2到2·n的所有数字和。将当前数字的数字存储在一个数组中,然后实现类似里程表的计数器。增加该计数器时,跟踪奇数和偶数总和。在10个案例中的9个案例中,您只需通过从相应的总和中删除其值并将下一个数字添加到另一个总和来调整最后一个数字。

这是一个执行此操作的程序。函数next递增计数器并保持偶数和奇数的数字和在sums[0]sums[1]中。主程序与你的程序基本相同,只是循环被分成两部分:一部分k增加,一部分减少。

even = set(range(0, 10, 2))

def next(num, sums):    
    o = num[0]
    if o in even:
        sums[0] -= o
        sums[1] += o + 1
    else:
        sums[0] += o + 1
        sums[1] -= o

    num[0] += 1

    i = 0
    while num[i] == 10:
        sums[0] -= 10
        num[i] = 0

        i += 1
        o = num[i]
        if o in even:
            sums[0] -= o
            sums[1] += o + 1
        else:
            sums[0] += o + 1
            sums[1] -= o

        num[i] += 1

n = int(raw_input())
total = 0

m = len(str(2 * n + 1))
num = [0] * m
num[0] = 2
sums = [2, 0]

k = 1
for i in range(2, n + 2):
    total += abs(sums[0] - sums[1]) * k
    k += 1
    next(num, sums)    

k = n        
for i in range(n + 2, 2*n + 1):
    k -= 1        
    total += abs(sums[0] - sums[1]) * k
    next(num, sums)

print total

我上面已经说过,备忘录对这种方法没有用。这不是真的。您可以存储数字i的偶数和奇数位和,并在计算10 * i10 * i + 9时使用它。当您按diff增加的顺序致电i时,您将可以访问i // 10的存储总和。

这并不比里程表方法快得多,但实施更加清晰,因为增加了内存。 (对于大n,预分配数组的效果优于字典。您无需为(2*n + 11) / 10以上的数字预留空间。)

def diff(s):
    d = s % 10

    e = ememo[s / 10]
    o = omemo[s / 10]

    if d in even:
        e += d
    else:
        o += d

    if s < smax:
        ememo[s] = e 
        omemo[s] = o    

    return e, o

n = int(raw_input())

total = 0

even = set(range(0, 10, 2))
smax = (2*n + 11) / 10
omemo = smax * [0]
ememo = smax * [0]
omemo[1] = 1

k = 1    
for i in range(2, n + 2):
    e, o = diff(i)
    total += abs(e - o) * k
    k += 1        

k = n        
for i in range(n + 2, 2*n + 1):
    k -= 1   
    e, o = diff(i)     
    total += abs(e - o) * k

print total

如果可以找到数字总和的封闭公式,这可以做得更快,但我认为绝对函数阻止了这样的解决方案。