/* Copyright 2009-2016 David Hadka * * This file is part of the MOEA Framework. * * The MOEA Framework is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * The MOEA Framework is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the MOEA Framework. If not, see <http://www.gnu.org/licenses/>. */ package org.moeaframework.core.variable; import java.util.BitSet; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.moeaframework.core.PRNG; import org.moeaframework.core.Variable; /** * Decision variable for binary strings. */ public class BinaryVariable implements Variable { private static final long serialVersionUID = -682157453241538355L; /** * The number of bits stored in this variable. */ private final int numberOfBits; /** * The internal storage for the bits. */ private final BitSet bitSet; /** * Constructs a binary variable with the specified number of bits. All bits * are initially set to {@code false}. * * @param numberOfBits the number of bits stored in this variable */ public BinaryVariable(int numberOfBits) { super(); this.numberOfBits = numberOfBits; bitSet = new BitSet(numberOfBits); } /** * Returns the number of bits stored in this variable. * * @return the number of bits stored in this variable */ public int getNumberOfBits() { return numberOfBits; } /** * Returns the number of bits in this variable set to {@code true}. * * @return the number of bits in this variable set to {@code true} */ public int cardinality() { return bitSet.cardinality(); } /** * Sets all bits in this variable to {@code false}. */ public void clear() { bitSet.clear(); } /** * Returns {@code true} if all bits in this variable are set to * {@code false}; {@code false} otherwise. * * @return {@code true} if all bits in this variable are set to * {@code false}; {@code false} otherwise */ public boolean isEmpty() { return bitSet.isEmpty(); } /** * Returns the value of the bit at the specified index. * * @param index the index of the bit to return * @return the value of the bit at the specified index * @throws IndexOutOfBoundsException if the index is out of bounds * {@code (index < 0) || (index >= getNumberOfBits())} */ public boolean get(int index) { if ((index < 0) || (index >= numberOfBits)) { throw new IndexOutOfBoundsException(); } return bitSet.get(index); } /** * Sets the value of the bit at the specified index. * * @param index the index of the bit to set * @param value the new value of the bit being set * @throws IndexOutOfBoundsException if the index is out of bounds * {@code (index < 0) || (index >= getNumberOfBits())} */ public void set(int index, boolean value) { if ((index < 0) || (index >= numberOfBits)) { throw new IndexOutOfBoundsException(); } bitSet.set(index, value); } /** * Returns a {@link BitSet} representing the state of this variable. * * @return a {@code BitSet} representing the state of this variable */ public BitSet getBitSet() { return (BitSet)bitSet.clone(); } /** * Returns the Hamming distance between this instance and the specified * {@code BinaryVariable}. The Hamming distance is the number of bit * positions in which the two binary strings differ. * * @param variable the other {@code BinaryVariable} * @return the Hamming distance between this instance and the specified * {@code BinaryVariable} * @throws IllegalArgumentException if the two binary strings differ in the * number of bits */ public int hammingDistance(BinaryVariable variable) { if (numberOfBits != variable.numberOfBits) { throw new IllegalArgumentException("must have same number of bits"); } int count = 0; for (int i = 0; i < numberOfBits; i++) { if (bitSet.get(i) != variable.bitSet.get(i)) { count++; } } return count; } @Override public BinaryVariable copy() { BinaryVariable copy = new BinaryVariable(numberOfBits); copy.bitSet.or(bitSet); return copy; } @Override public int hashCode() { return new HashCodeBuilder() .append(numberOfBits) .append(bitSet) .toHashCode(); } @Override public boolean equals(Object obj) { if (obj == this) { return true; } else if ((obj == null) || (obj.getClass() != getClass())) { return false; } else { BinaryVariable rhs = (BinaryVariable)obj; return new EqualsBuilder() .append(numberOfBits, rhs.numberOfBits) .append(bitSet, rhs.bitSet) .isEquals(); } } @Override public String toString() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < numberOfBits; i++) { sb.append(bitSet.get(i) ? "1" : "0"); } return sb.toString(); } @Override public void randomize() { for (int i = 0; i < getNumberOfBits(); i++) { set(i, PRNG.nextBoolean()); } } }