Is there a term for leveraging the fact that data is comprised of a few much-repeated values to speed computation?
As an example when trying to compute Sample Entropy on a long discrete sequence (Length=64.000.000.000, Distinct elements = 11, Length of substring=3) I was finding the running time too long (over 10 minutes). I realised that I should be able to make use of the relatively few distinct elements to speed up computation but was unable to find any literature relating to doing this (I suspect because I don't know what to Google).
The algorithm for Sample Entropy involves counting the pairs of substrings that are within a certain tolerance. This was the computationally expensive aspect of the algorithm O(n^2). By taking only the distinct substrings (of which there were at most 1331) I was able to find the pairs of distinct substrings within the tolerance, I then used the counts of each distinct substring to find the total number of pairs of (non-distinct) substrings that are within a certain tolerance. This method substantially sped up my computation.
Do algorithms that make use of the property of relatively few, much-repeated elements have a specific terminology.
def sampen(L, m, r):
N = len(L)
B = 0.0
A = 0.0
# Split time series and save all templates of length m
xmi = np.array([L[i : i + m] for i in range(N - m)])
xmj = np.array([L[i : i + m] for i in range(N - m + 1)])
# Save all matches minus the self-match, compute B
B = np.sum([np.sum(np.abs(xmii - xmj).max(axis=1) <= r) - 1 for xmii in xmi])
# Similar for computing A
m += 1
xm = np.array([L[i : i + m] for i in range(N - m + 1)])
A = np.sum([np.sum(np.abs(xmi - xm).max(axis=1) <= r) - 1 for xmi in xm])
# Return SampEn
return -np.log(A / B)
def sampen2(L, m, r):
N = L.shape[0]
# Split time series and save all templates of length m
xmi = np.array([L[i : i + m] for i in range(N - m)])
xmj = np.array([L[i : i + m] for i in range(N - m + 1)])
# Find the unique subsequences and their counts
uni_xmi, uni_xmi_counts = np.unique(xmi, axis=0, return_counts = True)
uni_xmj, uni_xmj_counts = np.unique(xmj, axis=0, return_counts = True)
# Save all matches minus the self-match, compute B
B = np.sum(np.array([np.sum((np.abs(unii - uni_xmi).max(axis=1) <= r)*uni_xmj_counts)-1 for unii in uni_xmi])*uni_xmi_counts)
# Similar for computing A
m +=1
xm = np.array([L[i: i + m] for i in range(N - m + 1)])
uni_xm, uni_xm_counts= np.unique(xm, axis=0, return_counts = True)
A = np.sum(np.array([np.sum((np.abs(unii - uni_xm).max(axis=1) <= r)*uni_xm_counts)-1 for unii in uni_xm])*uni_xm_counts)
return -np.log(A / B)