3

Let's say you want to make an asymmetric transformation on an array of data, but, you want your data to be symmetric in regard of this transformation. You would

  1. make a random permutation of your data,
  2. do your asymmetric transformation your permuted data,
  3. reverse the permutation to put your data back in place.

Problem : it is much easier to make the permutation on your original dataspace in order to put your data back in place, than to build a reversed permutation.


Example : to generate a random foreground color, which is far from a background color, you would need to be far from one of the three fundamental colors and both the other colors may be random. Let's say my background color is:

import random
background = tuple(random.randint(0, 0xff) for _ in range(3))

let's choose one component at random:

selector = random.choice(((0, 1, 2), (1, 0, 2), (2, 0 ,1)))

Then, I will choose my first component, far from that one:

far_component = 0xff if background[selector[0]] < 0x80 else 0

and then I will create my foreground color as a function of my initial selection:

foreground = list(range(3))
foreground[selector[0]] = far_component
foreground[selector[1]] = random.randint(0, 0xff)
foreground[selector[2]] = random.randint(0, 0xff)

In this simple case, things are quite manageable, but let's imagine a much more complex case with many more dimensions. It will require a loop to manage all the output assignations like the following:

variable = len(selector)*None # If ever variable doesn't exist yet.
for i in range(len(selector)):
    variable[selector[i]] = ordered_source_tuple[i]

Would there be a nice pythonic way to write such an assignation which would look like a comprehension list on variables. Something like this (which obviously won't work because the left part won't be a tuple of LValues):

(variable[i] for i in selector) = ordered_source_tuple

and finding the reverse permutation to write this is no simple operation

variable = ordered_source_tuple(reversed_selector)
Camion
  • 1,264
  • 9
  • 22
  • 1
    This is a great general question about lvalues in Python. However, could you perhaps simplify your code in this case by assigning random values to *every* foreground component, then overwriting one, at random, with `far_component`? – NicholasM Dec 05 '19 at 16:31
  • Well, thinking about it, it might ruin the example because my point is that there might be many more dimensions and many more than one non random value. But I simplified it another way. – Camion Dec 05 '19 at 16:39
  • Just to be clear, in your question, can `selector` and `ordered_source_tuple` have length less than the original sequence (`variable`)? – NicholasM Dec 05 '19 at 16:46
  • No, in this general case, all are the same size. I Just want to reverse the original permutation without having to build the reversed permutation selector. The general idea is : Make a random permutation - modify the permuted data according to a specific plan - put the modified data back in their original place. – Camion Dec 05 '19 at 16:49
  • I added info in the main question to reply your question – Camion Dec 05 '19 at 17:10

3 Answers3

0

Create the new color, then permute it before assigning to foreground.

import random
from operator import itemgetter


background = tuple(random.randint(0, 0xff) for _ in range(3))
selector = random.choice(((0, 1, 2), (1, 0, 2), (2, 0 ,1)))
far_component = 0xff if background[selector[0]] < 0x80 else 0

permute = itemgetter(*selector)
components = [far_component, random.randint(), 0xff), random.randint(0, 0xff)]
foreground = permute(components)
chepner
  • 497,756
  • 71
  • 530
  • 681
  • I believe you missed the point that is a simple example for something that might be much more complicated. – Camion Dec 13 '19 at 01:50
0

This might be cheating, but how about a dictionary:

variable = dict(zip(selector, ordered_source_tuple))

If you want to turn it back into an object sorted by key, you can do

variable_list = [variable[i] for i in range(len(selector))]
Juan Carlos Ramirez
  • 2,054
  • 1
  • 7
  • 22
  • It is not really what I was asking, but I liked this solution because I thought linearly accessing a list and then random accessing a dictionary might be quicker that random accessing a list like in my example, but having benchmarked it with %timeit in ipython, it appear that this is around two times slower than my loop. If you like, I could append my benchmarking code at the end of your solution. – Camion Dec 13 '19 at 01:38
0

This is no longer a one-liner, but if you define a helper function as in this answer, it becomes one:

def inverted_permutation(p):
    inverse = [0] * len(p)
    for i, p in enumerate(p):
        inverse[p] = i
    return inverse

variable = tuple(source[i] for i in inverted_permutation(selector))

Of course, this is more or less equivalent to your assignment loop, but it hides that in a somewhat general-purpose function.

NicholasM
  • 4,557
  • 1
  • 20
  • 47