Skip to content

Instantly share code, notes, and snippets.

@william-silversmith
Last active January 31, 2018 06:21
Show Gist options
  • Save william-silversmith/5d1668fffed7a71688932c44ee266414 to your computer and use it in GitHub Desktop.
Save william-silversmith/5d1668fffed7a71688932c44ee266414 to your computer and use it in GitHub Desktop.
Fully generalized COUNTLESS algorithm with a (relatively) memory efficient dynamic programming solution
from itertools import combinations
from functools import reduce
import numpy as np
def countlessND(data, factor):
assert len(data.shape) == len(factor)
sections = []
mode_of = reduce(lambda x,y: x * y, factor)
majority = int(math.ceil(float(mode_of) / 2))
data += 1 # offset from zero
# This loop splits the 2D array apart into four arrays that are
# all the result of striding by 2 and offset by (0,0), (0,1), (1,0),
# and (1,1) representing the A, B, C, and D positions from Figure 1.
for offset in np.ndindex(factor):
part = data[tuple(np.s_[o::f] for o, f in zip(offset, factor))]
sections.append(part)
pick = lambda a,b: a * (a == b)
lor = lambda x,y: x + (x == 0) * y # logical or
subproblems = [ {}, {} ]
results2 = None
for x,y in combinations(range(len(sections) - 1), 2):
res = pick(sections[x], sections[y])
subproblems[0][(x,y)] = res
if results2 is not None:
results2 = lor(results2, res)
else:
results2 = res
results = [ results2 ]
for r in range(3, majority+1):
r_results = None
for combo in combinations(range(len(sections)), r):
res = pick(subproblems[0][combo[:-1]], sections[combo[-1]])
if combo[-1] != len(sections) - 1:
subproblems[1][combo] = res
if r_results is not None:
r_results = lor(r_results, res)
else:
r_results = res
results.append(r_results)
subproblems[0] = subproblems[1]
subproblems[1] = {}
results.reverse()
final_result = lor(reduce(lor, results), sections[-1]) - 1
data -= 1
return final_result
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment