Invertable Cartesian Product Elements/Index Transl

2019-08-15 01:47发布

问题:

I have a problem where I need to identify the elements found at an indexed position within the Cartesian product of a series of lists but also, the inverse, i.e. identify the indexed position from a unique combination of elements from a series of lists.

I've written the following code which performs the task reasonably well:

import numpy as np

def index_from_combination(meta_list_shape, index_combination ):
    list_product = np.prod(meta_list_shape)
    m_factor = np.cumprod([[l] for e,l in enumerate([1]+meta_list_shape)])[0:len(meta_list_shape)]
    return np.sum((index_combination)*m_factor,axis=None)


def combination_at_index(meta_list_shape, index ):
    il = len(meta_list_shape)-1
    list_product = np.prod(meta_list_shape)
    assert index < list_product
    m_factor = np.cumprod([[l] for e,l in enumerate([1]+meta_list_shape)])[0:len(meta_list_shape)][::-1]
    idxl = []
    for e,m in enumerate(m_factor):
        if m<=index:
            idxl.append((index//m))
            index = (index%m)
        else:
            idxl.append(0)
    return idxl[::-1]

e.g.

index_from_combination([3,2],[2,1])
>> 5
combination_at_index([3,2],5)
>> [2,1]

Where [3,2] describes a series of two lists, one containing 3 elements, and the other containing 2 elements. The combination [2,1] denotes a permutation consisting of the 3rd element (zero-indexing) from the 1st list, and the 2nd element (again zero-indexed) from the second list.

...if a little clunkily (and, to save space, one that ignores the actual contents of the lists, and instead works with indexes used elsewhere to fetch the contents from those lists - that's not important here though).

N.B. What is important is that my functions mirror one another such that:

F(a)==b   and G(b)==a  

i.e. they are the inverse of one another.

From the linked question, it turns out I can replace the second function with the one-liner:

list(itertools.product(['A','B','C'],['P','Q','R'],['X','Y']))[index]

Which will return the unique combination of values for a supplied index integer (though with some question-mark in my mind about how much of that list is instantiated in memory - but again, that's not necessarily important right now).

What I'm asking is, itertools appears to have been built with these types of problems in mind - is there an equally neat one-line inverse to the itertools.product function that, given a combination, e.g. ['A','Q','Y'] will return an integer describing that combination's position within the cartesian product, such that this integer, if fed into the itertools.product function will return the original combination?

回答1:

Imagine those combinations as two dimensional X-Y coordinates and use subscript to linear-index conversion and vice-verse. Thus, use NumPy's built-ins np.ravel_multi_index for getting the linear index and np.unravel_index for the subscript indices, which becomes your index_from_combination and combination_at_index respectively.

It's a simple translation and doesn't generate any combination whatsoever, so should be a breeze.

Sample run to make things clearer -

In [861]: np.ravel_multi_index((2,1),(3,2))
Out[861]: 5

In [862]: np.unravel_index(5, (3,2))
Out[862]: (2, 1)

The math is simple enough to be implemented if you don't want to NumPy dependency for some reason -

def index_from_combination(a, b):
    return b[0]*a[1] + b[1]

def combination_at_index(a, b):
    d = b//a[1]
    r = b - a[1]*d
    return d, r

Sample run -

In [881]: index_from_combination([3,2],[2,1])
Out[881]: 5

In [882]: combination_at_index([3,2],5)
Out[882]: (2, 1)