问题
The task description is here: https://codility.com/demo/take-sample-test/peaks It's also here: Codility Peaks Complexity
First, I tried solving this myself but was only able to come up with what I believed to be a brute force solution. However, it scored 100/100: https://codility.com/demo/results/demoRNWC3P-X4U
Which obviously is completely unsatisfying for me. ;) The outer loop is called for each factor of N
(or for each peak, whichever is smaller) and the inner one is called for each peak (just checking if there's a peak in every block). Maybe that's O(N^2)
, maybe a bit better (since it passes the tests in time limits) but I'm almost sure it's not O(N*log(log(N)))
.
Then I tried searching for an O(N*log(log(N)))
solution but everyone else seems to have a pretty similar solution to mine.
So does anyone have an idea for an O(N*log(log(N)))
(or a better one) solution?
Also, if anyone could tell me what complexity my solution has I'd be grateful.
回答1:
Your code is O(n d(n)) where d(n) is the number of divisors of n. On [1, 100000], d(n) is maximised at 83160 = 2^3 3^3 5 7 11, which has 126 divisors. According to Wikipedia, d(n) is o(n^epsilon) for every epsilon>0, so it grows rather slowly.
To get an O(n log log n) solution, build a partial sum array telling you how many peaks are left of each point. Then you can tell whether there's a peak in an interval in O(1) time. Checking a divisor d then takes O(n/d) time. Adding up n/d over all divisors d is the same as adding up d over all divisors d, and the result is, according to the same Wikipedia page, O(n log log n).
回答2:
I've implemented a solution in the style suggested by tmyklebu (thanks!) which should be n.log(log(n)). Codility no longer test 'performance' on this problem (!) but the python solution scores 100% for accuracy.
In passing, if you've been doing the Codility Lessons you'll remember from the Lesson 8: Prime and composite numbers that the sum of harmonic number operations will give O(log(n)) complexity. We've got a reduced set, because we're only looking at factor denominators. Lesson 9: Sieve of Eratosthenes shows how the sum of reciprocals of primes is O(log(log(n))) and claims that 'the proof is non-trivial'. The sum of divisor reciprocals is different to the sum of prime reciprocals but I'd suggest that it falls into the 'non-trivial' proof category too!
def solution(data):
length = len(data)
# array ends can't be peaks, len < 3 must return 0
if len < 3:
return 0
peaks = [0] * length
# compute a list of 'peaks to the left' in O(n) time
for index in range(2, length):
peaks[index] = peaks[index - 1]
# check if there was a peak to the left, add it to the count
if data[index - 1] > data[index - 2] and data[index - 1] > data[index]:
peaks[index] += 1
# candidate is the block size we're going to test
for candidate in range(3, length + 1):
# skip if not a factor
if length % candidate != 0:
continue
# test at each point n / block
valid = True
index = candidate
while index != length:
# if no peak in this block, break
if peaks[index] == peaks[index - candidate]:
valid = False
break
index += candidate
# one additional check since peaks[length] is outside of array
if index == length and peaks[index - 1] == peaks[index - candidate]:
valid = False
if valid:
return length / candidate
return 0
来源:https://stackoverflow.com/questions/26150180/onloglogn-algorithm-for-codilitys-peaks