简体   繁体   English

生成不重复的,间隔开的RGB颜色值的算法

[英]Algorithm for generating not repeating, spaced-out RGB color values

I want to have a static method, which whenever called will return a color value that didn't appear yet, and is not too close to last returned color (ie return new Color(last_value += 10) won't do). 我想有一个静态方法,该方法每次调用时都将返回尚未出现的颜色值,并且该颜色值与上次返回的颜色不太接近(即return new Color(last_value += 10)不会)。 It also should be not random, so everytime the application is launched, the sequence of returned colors would be the same. 它也不应该是随机的,因此每次启动该应用程序时,返回的颜色顺序都是相同的。

First thing that popped to my head, is to iterate over an array with a primal number step, something like this: 突然出现在我头上的是,要遍历具有原始数阶的数组,如下所示:

    private static HashMap<Integer, Boolean> used = new HashMap<>();
    private static int[] values = new int[0xfffff]; // 1/16th of possible colors
    private static int current = 0, jump = values.length / 7;

     public static Color getColour(){
        int value = values[current];
        used.put(current, true);
        current += jump;
        current %= values.length;
        //have some check here if all colors were used
        while (used.containsKey(current)){
            current++;
            current%=values.length;
        }
        return new Color(value);
    }

But I don't like it, since colors will be close to each other from some calls back. 但我不喜欢它,因为某些回叫会导致颜色彼此接近。

A good way of generating non-repeating random-like sequences is using an LFSR . 生成非重复的类似随机序列的一种好方法是使用LFSR

/**
 * Linear feedback shift register
 *
 * Taps can be found at: See http://www.xilinx.com/support/documentation/application_notes/xapp052.pdf See http://mathoverflow.net/questions/46961/how-are-taps-proven-to-work-for-lfsrs/46983#46983 See
 * http://www.newwaveinstruments.com/resources/articles/m_sequence_linear_feedback_shift_register_lfsr.htm See http://www.yikes.com/~ptolemy/lfsr_web/index.htm See
 * http://seanerikoconnor.freeservers.com/Mathematics/AbstractAlgebra/PrimitivePolynomials/overview.html
 *
 * @author OldCurmudgeon
 */
public class LFSR implements Iterable<BigInteger> {

    // Bit pattern for taps.
    private final BigInteger taps;
    // Where to start (and end).
    private final BigInteger start;

    // The poly must be primitive to span the full sequence.
    public LFSR(BigInteger primitivePoly, BigInteger start) {
        // Where to start from (and stop).
        this.start = start.equals(BigInteger.ZERO) ? BigInteger.ONE : start;
        // Knock off the 2^0 coefficient of the polynomial for the TAP.
        this.taps = primitivePoly.shiftRight(1);
    }

    public LFSR(BigInteger primitivePoly) {
        this(primitivePoly, BigInteger.ONE);
    }

    // Constructor from an array of taps.
    public LFSR(int[] taps) {
        this(asPoly(taps));
    }

    private static BigInteger asPoly(int[] taps) {
        // Build the BigInteger.
        BigInteger primitive = BigInteger.ZERO;
        for (int bit : taps) {
            primitive = primitive.or(BigInteger.ONE.shiftLeft(bit));
        }
        return primitive;
    }

    @Override
    public Iterator<BigInteger> iterator() {
        return new LFSRIterator(start);
    }

    private class LFSRIterator implements Iterator<BigInteger> {
        // The last one we returned.

        private BigInteger last = null;
        // The next one to return.
        private BigInteger next = null;

        public LFSRIterator(BigInteger start) {
            // Do not return the seed.
            last = start;
        }

        @Override
        public boolean hasNext() {
            if (next == null) {
                /*
                 * Uses the Galois form.
                 *
                 * Shift last right one.
                 *
                 * If the bit shifted out was a 1 - xor with the tap mask.
                 */
                boolean shiftedOutA1 = last.testBit(0);
                // Shift right.
                next = last.shiftRight(1);
                if (shiftedOutA1) {
                    // Tap!
                    next = next.xor(taps);
                }
                // Never give them `start` again.
                if (next.equals(start)) {
                    // Could set a finished flag here too.
                    next = null;
                }
            }
            return next != null;
        }

        @Override
        public BigInteger next() {
            // Remember this one.
            last = hasNext() ? next : null;
            // Don't deliver it again.
            next = null;
            return last;
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException("Not supported.");
        }

        @Override
        public String toString() {
            return LFSR.this.toString()
                    + "[" + (last != null ? last.toString(16) : "")
                    + "-" + (next != null ? next.toString(16) : "") + "]";
        }
    }

    @Override
    public String toString() {
        return "(" + taps.toString(32) + ")-" + start.toString(32);
    }

}

Now you just need an 8+8+8=24 tap value. 现在,您只需要一个8+8+8=24抽头值即可。

Using it is simple. 使用起来很简单。

public void test() {
    // Sample 24-bit tap found on page 5 of
    // http://www.xilinx.com/support/documentation/application_notes/xapp052.pdf
    int[] taps = new int[]{24, 23, 22, 17};
    LFSR lfsr = new LFSR(taps);
    int count = 100;
    for (BigInteger i : lfsr) {
        System.out.println("Colour: " + new Color(i.intValue()));
        if (--count <= 0) {
            break;
        }
    }
}

Features of an LFSR: LFSR的特点:

  • It will repeat - but not until all possible bit patterns have been generated (except 0 ). 重复-但直到所有可能的位模式都生成( 0除外)后,它才会重复。
  • The number generated is statistically a good random number. 从统计上来说,生成的数字是一个很好的随机数。
  • The same sequence will be generated each time (choose a different tap if you want a different sequence). 每次都会生成相同的序列(如果需要不同的序列,请选择不同的tap )。

To achieve the spacing you require I would suggest you add a filter and discard any that are too close (by your criterion) to the previous one. 为了达到所需的间距,我建议您添加一个过滤器,并丢弃与上一个过滤器过于接近的过滤器(根据您的标准)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM