my question is
if i have only 3 basic colors (each made of rgb):
color1 : R:150, B:zero, G:255
color2 : R:255, B:150, G:zero
color3 : R:zero, B:255, G:150
They can be mixed using the formula :
new_color = floor(X*0.9)+floor(Y*0.1)
X and Y can be a basic color or a new color already created by using the formula.
for example, if i want to mix color1 as main with color3 :
new_color(R,B,G) = (floor(0.9*150)+floor(0.1*0) , floor(0.9*0)+floor(0.1*255) , floor(0.9*255)+floor(0.1*150) ) = (135, 25, 244).
I need to find a way to mix many colors in order to get a desired color, for example : R:187 B:135 G:201
so far i wrote a "brute force" program which go all over the combinations of basic colors (runing for 7 days now got up to 16 mixing steps) and a bit smarter AStar algorithm (got good reults for small sequnces, letting it run for the week end…).
Since i know some colors are made of more then 100 mixing steps, it will take me forever to go over all of the results…
Help me find a smarter and faster way to solve this.
Thanks.
Best Answer
(EDIT: I've completely rewritten this first algorithm for clarity)
If an approximate answer is good enough, I think there is a simple algorithm. First, consider the exact version of your problem:
ExactMix(C1, C2) = 0.9 * C1 + 0.1 * C2
.We can represent any color as a unique linear combination of the basic colors:
A slight modification that will simplify things is if we change from
(R,G,B)
coordinates to(x,y,z)
coordinates byIn
(x,y,z)
coordinates, we haveand
ExactMix
has the same form in(x,y,z)
coordinates as it does in(R,G,B)
-coordinates.The thing to note is that given any three colors in the
z=0
plane, you can useExactMix
to form arbitrarily good approximations to any color within the triangle they define. The algorithm is essentially a binary search: if the colorT
lies in the trianglePQR
, then letS=ExactMix(P,Q)
. Then eitherT
lies inSQR
or it lies inPSR
.By iterating this algorithm (and rotating which edge we split with
ExactMix
), we get an infinite sequence of triangles whose corners converge toT
.If we are so inclined, we might consider splitting the segment
PQ
into three segments:P-ExactMix(P,Q)
,ExactMix(P,Q)-ExactMix(Q,P)
, andExactMix(Q,P)-Q
rather than just two segments, and choose which of the three trianglesT
lies within.Now,
Mix
introduces round-off error. We can still use the same binary search algorithm to get the(x,y)
coordinates approximately right, but it will also spontaneously decrement thez
coordinate as well.If our target
z
coordinate is very negative, we can use the same binary search algorithm to get the(x,y)
coordinates right, then keep doing calculations until round-off error gives us the correctz
coordinate. (this might take some cleverness to avoid getting stuck in a situation where there is no round-off error)If our target
z
coordinate is nearly zero, we can still run the algorithm and hope for the best, getting something that is at least close to our target color.When implementing the inexact version, it is probably best to redo the coordinate change every time you pick a new triangle. That is, rather than always using the
(x,y,z)
coordinates defined by the initial three colors, you instead compute a new set of(x,y,z)
coordinates based off of your current set of three colors.For an exact answer, I expect exhaustive brute force to be feasible if done efficiently: a one-time calculation to compute every color you can possibly produce.
The natural data structure to hold the results would be to keep a structure that stores, for every color, one of the following three factoids:
Then keep a second list of every color ever produced. Initialize it with your three basic colors, then execute the following algorithm:
Efficient computation of the
mix
function is essential. You can probably get some advantage from the fact that your inner loop is overj
withi
fixed (and the compiler might recognize that for you). Also, you will need an efficient data structure for the main data. For example, you could store the data as follows:256x256x256
array ofpair<color,color>
256x256x256
bit array to represent which entries are "do not know how to make"The point of the bit array is that it will fit in cache (or nearly so), so that accessing it should be fast. Note that in the above algorithm you don't need the list of basic colors, so you could completely omit that part for your calculation.
It may or may not be slightly better to replace the first array with an array of 'pointers', and keep a second, flat array around of all of the pairs ever stored. This should result in a more compact data structure, since a large fraction of colors will never be produced, and may be faster to create. (less cache pollution)
If every color was reachable, then this algorithm would require
2^48
steps. Even if you could get the time of the inner loop down to a dozen ticks on a 1 GHz computer, it would take 36 days.However, a large fraction of the colors are not reachable from the basic colors, and the algorithm designed above is meant to calculate only the entries that are reachable. I don't have the tools handy to estimate the fraction easily, but I fully expect the calculation to take only a few days with a very well optimized inner loop. A C/C++ implementation (or Fortran, I suppose) is probably a requirement for this calculation (unless it happens to be even smaller than I expect it to be).
Also, you could parallelize the algorithm, so as to use multiple cores on your CPU or multiple computers. It will be a little tricky to do so, as there are two obstacles: