Pergunta

Eu estou escrevendo um pedaço de tempo crítico de código em C # que me obriga a converter dois inteiros sem sinal que definem um intervalo inclusivo em um campo de bits. Ex:

uint x1 = 3;
uint x2 = 9;
  //defines the range [3-9]
  //                              98  7654 3
  //must be converted to:  0000 0011  1111 1000

Ela pode ajudar a visualizar os bits em ordem inversa

O valor máximo para esta faixa é um parâmetro dado no tempo de execução que chamaremos max_val. Portanto, o campo de bits variável deve ser definido como uma matriz de UInt32 com tamanho igual a max_val/32:

UInt32 MAX_DIV_32 = max_val / 32;
UInt32[] bitArray = new UInt32[MAX_DIV_32];

Dado um intervalo definido pelas variáveis ??x1 e x2, qual é o caminho mais rápido para executar esta conversão?

Foi útil?

Solução

Tente isto. Calcular a gama de itens de matriz que devem ser preenchidos com todos aqueles e fazer isso por iteração sobre esta faixa. Finalmente definir os itens em ambas as fronteiras.

Int32 startIndex = x1 >> 5;
Int32 endIndex = x2 >> 5;

bitArray[startIndex] = UInt32.MaxValue << (x1 & 31);

for (Int32 i = startIndex + 1; i <= endIndex; i++)
{
   bitArray[i] = UInt32.MaxValue;
}

bitArray[endIndex] &= UInt32.MaxValue >> (31 - (x2 & 31));

Pode ser o código não é 100% correto, mas a idéia deve funcionar.


Apenas testado e encontrou três bugs. O cálculo em índice inicial necessária uma modificação 32 e no índice de extremidade 32 deve ser 31 e um lógico e em vez de uma atribuição de lidar com o caso de início e fim de índice ser a mesma. Deve ser bastante rápido.


Apenas aferido com igual distribuição de x1 e x2 sobre a matriz. Intel Core 2 Duo E8400 3.0 GHz, MS VirtualPC com Server 2003 R2 no host Windows XP.

Array length [bits]           320         160         64
Performance [executions/s]    33 million  43 million  54 million

Mais uma optimazation x% 32 == x & 31, mas eu sou incapaz de meassure um ganho de desempenho. Por causa de apenas 10.000.000 iterações no meu teste as flutuações são bastante elevados. E eu estou correndo em VirtualPC tornando a situação ainda mais imprevisível.

Outras dicas

A minha solução para definir toda uma gama de bits em um BitArray para true ou false:

public static BitArray SetRange(BitArray bitArray, Int32 offset, Int32 length, Boolean value)
{

    Int32[] ints = new Int32[(bitArray.Count >> 5) + 1];

    bitArray.CopyTo(ints, 0);

    var firstInt = offset >> 5;
    var lastInt = (offset + length) >> 5;

    Int32 mask = 0;

    if (value)
    {
        // set first and last int
        mask = (-1 << (offset & 31));
        if (lastInt != firstInt)
            ints[lastInt] |= ~(-1 << ((offset + length) & 31));
        else
            mask &= ~(-1 << ((offset + length) & 31));

        ints[firstInt] |= mask;

        // set all ints in between
        for (Int32 i = firstInt + 1; i < lastInt; i++)
            ints[i] = -1;
    }

    else
    {
        // set first and last int
        mask = ~(-1 << (offset & 31));
        if (lastInt != firstInt)
            ints[lastInt] &= -1 << ((offset + length) & 31);
        else
            mask |= -1 << ((offset + length) & 31);

        ints[firstInt] &= mask;

        // set all ints in between
        for (Int32 i = firstInt + 1; i < lastInt; i++)
            ints[i] = 0;

    }

    return new BitArray(ints) { Length = bitArray.Length };

}

Você pode tentar:

UInt32 x1 = 3;
UInt32 x2 = 9;
UInt32 newInteger = (UInt32)(Math.Pow(2, x2 + 1) - 1) & 
                   ~(UInt32)(Math.Pow(2, x1)-1);

Existe uma razão para não usar a classe System.Collections.BitArray em vez de um UInt32 []? Caso contrário, eu tentaria algo como isto:

int minIndex = (int)x1/32;
int maxIndex = (int)x2/32;
// first handle the all zero regions and the all one region (if any)
for (int i = 0; i < minIndex; i++) {
    bitArray[i] = 0;
}
for (int i = minIndex + 1; i < maxIndex; i++) {
    bitArray[i] = UInt32.MaxValue; // set to all 1s
}
for (int i = maxIndex + 1; i < MAX_DIV_32; i++) {
    bitArray[i] = 0;
}

// now handle the tricky parts
uint maxBits = (2u << ((int)x2 - 32 * maxIndex)) - 1; // set to 1s up to max
uint minBits = ~((1u << ((int)x1 - 32 * minIndex)) - 1); // set to 1s after min

if (minIndex == maxIndex) {
    bitArray[minIndex] = maxBits & minBits;
}
else {
    bitArray[minIndex] = minBits;
    bitArray[maxIndex] = maxBits;
}

Eu estava entediado o suficiente para tentar fazê-lo com uma matriz char e usando Convert.ToUInt32(string, int) para converter em um uint de base 2.

uint Range(int l, int h)
{
  char[] buffer = new char[h];
  for (int i = 0; i < buffer.Length; i++)
  {
    buffer[i] = i < h - l ? '1' : '0';
  }
  return Convert.ToUInt32(new string(buffer), 2);
}

Um simples mostra de referência que meu método é cerca de 5% mais rápido do que Angrey Jim (mesmo se você substituir segunda Pow com uma mudança pouco.)

É provavelmente o mais fácil de converter para produzir uma matriz uint se o limite superior é grande demais para caber em um único int. É um pouco enigmática mas acredito que ele funciona.

uint[] Range(int l, int h)
{
  char[] buffer = new char[h];
  for (int i = 0; i < buffer.Length; i++)
  {
    buffer[i] = i < h - l ? '1' : '0';
  }

  int bitsInUInt = sizeof(uint) * 8;
  int numNeededUInts = (int)Math.Ceiling((decimal)buffer.Length /
                                         (decimal)bitsInUInt);
  uint[] uints = new uint[numNeededUInts];
  for (int j = uints.Length - 1, s = buffer.Length - bitsInUInt;
       j >= 0 && s >= 0;
       j--, s -= bitsInUInt)
  {
    uints[j] = Convert.ToUInt32(new string(buffer, s, bitsInUInt), 2);
  }

  int remainder = buffer.Length % bitsInUInt;
  if (remainder > 0)
  {
    uints[0] = Convert.ToUInt32(new string(buffer, 0, remainder), 2);
  }

  return uints;
}

Tente isto:

uint x1 = 3;
uint x2 = 9;

int cbToShift = x2 - x1; // 6
int nResult = ((1 << cbToShift) - 1) << x1; 

/*
  (1<<6)-1 gives you 63 = 111111, then you shift it on 3 bits left
*/
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top