2

如果我有数字 1、2、3、4、5 和 6,并且我想以尽可能最小的方式存储它们的某种组合,我会怎么做?

例如,我可能想要存储 1、4 和 5。或者可能是 2、4、5 和 6。或者甚至可能是所有六个数字。我将始终至少有一个需要存储的号码。我想我可能已经看到通过位移实现了这一点,但我并不完全理解它是如何工作的。对于它的价值,我的最终目标是节省绝对最大的空间,因为这些值需要存储在具有极小存储空间的硬件设备上。

编辑 - - - - - - - - - - - - - - - - - - - -

感谢大家提出的所有伟大建议。我只是想澄清一下,我的应用程序中的实现不一定需要尽可能小,只要我理解并且对我身后的另一个开发人员有意义。最重要的是能够以尽可能最小的方式表示这些值,因为我最终必须用其他几个值构建一个字节数组,并将其全部写入存储空间非常有限的设备。再次感谢大家的精彩建议!

4

7 回答 7

6
[Flags]
public enum NumberEnum : byte
{
    None = 0,
    One = 1,
    Two = 2,
    Three = 4,
    Four = 8,
    Five = 16,
    Six = 32
};

public string ExampleMethodWithNumberCombinationsAsAEnum(NumberEnum filterFlags = 0)
{
 if ((filterFlags & NumberEnum.One) == NumberEnum.One)
 {
    //Do something with one
 }

 if (((filterFlags & NumberEnum.One) == NumberEnum.One) && ((filterFlags & NumberEnum.Two) == NumberEnum.Two))
 {
    //Do something with one & two
 }
}
于 2013-01-14T00:26:53.127 回答
1

你说过你需要节省空间,但你没有提到 RAM。这是一种需要更多虚拟内存的方法,但可以让您编写更简单的代码。

readonly Dictionary<int, int> _dictionary =
        Enumerable.Range(1, 6).ToDictionary(i => i, i => 1 << i - 1);

int GetFlags(params int[] ints)
{
    //Do checks on the dictionary, etc.
    return ints.Aggregate(0, (current, i) => current | _dictionary[i]);
}

然后,您可以通过以下方式使用代码:

var a = 1;
var b = 4;
var c = 5;
var result = GetFlags(a, b, c);

或者,GetFlags可以通过以下方式重写的主体:

var result = 0;
foreach (var i in ints)
    result |= _dictionary[i];
return result;
于 2013-01-14T00:32:03.120 回答
1
[Flags]
public enum UIntEnum : byte
{
    None = 0x0,
    One = 0x1,
    Two = 0x2,
    Three = 0x4,
    Four = 0x8,
    Five = 0x10,
    Six = 0x20
};

public static class UIntEnumExtensions
{
    public static Boolean ContainsOne(this UIntEnum enum)
    {
        // For .NET < 4.0
        // return ((enum & UIntEnum.One) == UIntEnum.One);
        // For .NET >= 4.0
        return enum.HasFlag(UIntEnum.One);
    }

    public static Boolean ContainsTwo(this UIntEnum enum)
    {
        // For .NET < 4.0
        // return ((enum & UIntEnum.Two) == UIntEnum.Two);
        // For .NET >= 4.0
        return enum.HasFlag(UIntEnum.Two);
    }

    // And so on...

    public static List<UInt32> GetComponents(this UIntEnum enum)
    {
        List<UInt32> values = new List<UInt32>();

        if (enum.ContainsOne())
            values.Add((UInt32)1);

        if (enum.ContainsTwo())
            values.Add((UInt32)2);

        // And so on...
    }
}

然后,例如:

UIntEnum enum = UIntEnum.Two | UIntEnum.Six;

if (enum.ContainsSix())
    Console.WriteLine("Enum contains Six!");

foreach (UInt32 value in enum.GetComponents())
    Console.WriteLine("Enum contains " + value.ToString() + "!");
于 2013-01-14T00:37:49.080 回答
1

您可以使用字节的六位来存储组合,这意味着您可以在每个字节中存储组合的三分之一和三分之一,或者在三个字节中存储四种组合:

+--------+--------+--------+
|12345612|34561234|56123456|
+--------+--------+--------+

要将值数组转换为 6 位值:

public static int GetCombination(int[] combination) {
  int n = 0;
  foreach (int a in combination) {
    switch (a) {
      case 1: n |= 1;
      case 2: n |= 2;
      case 3: n |= 4;
      case 4: n |= 8;
      case 5: n |= 16;
      case 6: n |= 32;
    }
  }
  return n;
}

要将其中四个值组合成三个字节:

public static byte[] PackCombinations(int[] values) {
  byte[] result = new byte[3];
  result[0] = (byte)((values[0] << 2) | (values[1] >> 4));
  result[1] = (byte)((values[1] << 4) | (values[2] >> 2));
  result[2] = (byte)((values[2] << 6) | (values[3]));
  return result;
}
于 2013-01-14T00:49:15.160 回答
1

不难想象你可以将它存储在一个bools 数组中。现在,考虑一下,什么是一点?一个位可以代表两种状态,就像一个bool. 现在,整数由位组成。我所知道的 C# 可用的最小的一个是byte. Abyte有八位。您可以将其中一个位用于您必须存储的每个数字。让我们从最低有效位到最高有效位编号。我们可以将 1 的存在存储在第 0 位中,将 2 存储在第 1 位中,将 3 存储在第 2 位中,等等。现在我们有了数据的表示。

我们如何打包数据?你提到了位移,你是对的。您可能想要使用位移,但您可能还需要使用一些其他操作;尤其是~(NOT)、&(AND) 和|(OR)。

总之,你会有这样的事情:

byte flags = 0;

// Let's add 2.
flags |= 1 << (2 - 1);

// Is 2 in it?
if(flags & (1 << (2 - 1)) != 0) {
    // Yes.
}else{
    // No.
}

// Let's remove 2.
flags &= ~(1 << (2 - 1));

这是如何运作的?如果1(位位置 0 中的 1)表示1存在,我们可以将其左移位位置时间以在该位位置获得 1。将两个字节进行或运算将得到集合的并集;ANDing 将采取交叉点。a & ~b从 中删除所有设置的ba

于 2013-01-14T00:29:47.747 回答
0

如果C# 可以处理 C++ STL 的东西(不知道),请参阅std::bitset-一个参考

否则,是的,只需一个热位就可以将它们全部放在一个字节中。

于 2013-01-14T00:30:57.867 回答
0

您可以尝试以下存储算法。它只需要一个 6 位的盒子。

class StorageBox
    {
        bool[] box = new bool[] { false, false, false, false, false, false };

        public void Addtobox(int number)
        {
            if (number<7 && number >0)
                box[number - 1] = true;
        }

        public string WhatIsinBox()
        {
            string result = "";

            for (int i = 0; i <= 5; i++)
            {
                if (box[i])
                    result = result + (i+1).ToString() + ",";

            }
            return result.Substring(0, result.Length - 1);
        }

        public void ClearBox()
        {
            box = new bool[] { false, false, false, false, false, false };
        }
    }


    class ExecuteSample
    {
        static void Main(string[] args)
        {
            var box = new StorageBox();

            box.Addtobox(5);
            box.Addtobox(3);
            box.Addtobox(4);

            Console.WriteLine(box.WhatIsinBox());

            Console.Read();
        }


    }
于 2013-01-18T17:30:13.463 回答