-
Notifications
You must be signed in to change notification settings - Fork 0
/
min_abs_sum.py
118 lines (93 loc) · 3.52 KB
/
min_abs_sum.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# -*- coding: utf-8 -*-
"""
Author: Niall O'Connor
#https://app.codility.com/programmers/lessons/17-dynamic_programming/min_abs_sum/
For a given array A of N integers and a sequence S of N integers from the set
{−1, 1}, we define val(A, S) as follows:
val(A, S) = |sum{ A[i]*S[i] for i = 0..N−1 }|
(Assume that the sum of zero elements equals zero.)
For a given array A, we are looking for such a sequence S that minimizes val(A,S).
Write a function:
def solution(A)
that, given an array A of N integers, computes the minimum value of val(A,S)
from all possible values of val(A,S) for all possible sequences S of N integers
from the set {−1, 1}.
For example, given array:
A[0] = 1
A[1] = 5
A[2] = 2
A[3] = -2
your function should return 0, since for S = [−1, 1, −1, 1], val(A, S) = 0,
which is the minimum possible value.
Write an efficient algorithm for the following assumptions:
N is an integer within the range [0..20,000];
each element of array A is an integer within the range [−100..100].
100% solution https://app.codility.com/demo/results/trainingNVW2NJ-SSA/
O(N * max(abs(A))**2)
"""
import time
def solution(A):
"""
Sum of A is a measure of how far away from 0 you can go.
Sum of A // 2 is how far you can go up and then back down.
Positives on the way up and negatives on the way down.
Array S is a red herring!
"""
n = len(A)
if n < 2: # quick base case
return 0 if not n else A[0]
# Set all values to absolute and total the array in one pass.
maximum = 0
for i in range(n):
A[i] = abs(A[i])
maximum += A[i]
# Array
A.sort()
mid_point = maximum // 2
A_left = A_right = 0 # For even stacking
O_left = O_right = 0 # For on side first stacked
#print(f'\nmaximum: {maximum}, mid_point: {mid_point}, A:{A} ')
for i in range(n-1, -1, -1):
# using and to evenly stack left and right
if A_right + A[i] <= mid_point and A_right < A_left:
A_right += A[i]
else:
A_left += A[i]
# using or to stack right fully before left
if O_right + A[i] <= mid_point or O_right < O_left:
O_right += A[i]
else:
O_left += A[i]
#print(f'A_left: {A_left}, A_right: {A_right}')
#print(f'O_left: {O_left}, O_right: {O_right}')
return min(abs(A_left - A_right), abs(O_left - O_right))
if __name__ == '__main__':
tests = (
#( expected, args )
(1, ([1, 3, 3, 4],)),
(0, ([],)),
(0, ([2,2],)),
(1, ([-2,1],)),
(0, ([2, 3, 2, 2, 3],)),
(0, ([1, 5, -2, 5, 2, 3],)),
(0, ([5, 5, 5, 4, 3, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1],)),
(0, ([7, 5, 7, 5, 7, 5, 3, 1, -2],)),
(1, ([6, 9, -6, 9, 9, -6, 9, -9, 6, 0, 6, 6, 4, 6, 6],)),
(2, ([48, 12, 12, 6, 6, 6, 6, 1, 3],)),
(36, ([67, 7, 5, 3, 4, 2, 1, 6, 3],)),
(3, ([7, 7, 7, 4, 4, 4],)),
(1, ([5, 2, 7, 5],)),
(0, ([18,99,-50,100,4]*4,)),
)
for expected, args in tests:
tic = time.perf_counter()
res = solution(*args)
toc = time.perf_counter()
if expected is None:
print(f'SPEED-TEST {len(args[0])} args finished in {toc - tic:0.8f} seconds')
continue # This is just a speed test
print(f'ARGS produced {res} in {toc - tic:0.8f} seconds')
try:
assert(expected == res)
except AssertionError as e:
print(f'ERROR {args} produced {res} when {expected} was expected!\n')