【问题标题】:I want to calculate all binary numbers up untill x bits with y set bits我想计算所有二进制数,直到 x 位和 y 位
【发布时间】:2021-08-07 20:11:03
【问题描述】:

有没有一种方法可以轻松地计算出具有特定设置位数量的所有数字的序列?

例如我想将所有 2 位的数字设置为最多 4 位:

3: has 2 bits set to true
5: ,,
6: ,,
9: ,,
10: ,,
12: ,,

有没有办法在不手动计算位数的情况下确定这些数字?

编辑: 数字的顺序并不重要。我确实想知道获得所有这些的最快方法,尽管对于特定数量的位数和最大位数。 (我不需要可以确定这个序列中第n个数字的方法)

编辑2: 我想要的原因是能够获得列表中的元素组合,如下所示:All Possible Combinations of a list of Values。 此解决方案将提供所有组合,其中我只想要具有 8 个唯一值的组合。

【问题讨论】:

  • 您是否在考虑特定的“序列”?还是顺序无关紧要?
  • 顺序无关紧要。理论上我可以在 :) 之后对其进行排序。
  • 我添加了 C# 语言,因为这是我想要编写的语言,但我想我可以用大多数语言阅读它。
  • 您是否愿意接受横向解决方案,也就是“作弊”?我正在考虑诸如查找表之类的非解决方案。我假设您想要可扩展性或有其他算法解决方案的原因。但您可能需要指定。
  • 我想要这个的主要原因是能够对此进行改进:stackoverflow.com/questions/7802822/… 我确实想在一个包含 12 个项目的列表中找到 8 个独特元素的所有组合。 (此功能将返回所有可能的组合)。所以关于作弊,我现在发现的最简单的方法是简单地循环数字并取所有 8 位的数字。不过这个数字会很快变大,所以我认为作弊很快就会导致数字变得太大。

标签: c# math


【解决方案1】:

是的,在n 位置生成k 设置位的所有组合存在位技巧。

Described here.

using System;

public class Test
{
    public static void Main()
    {
        int k = 2;
        int n = 4;
        int v = (1 << k) - 1;
        int finish = v << (n - k);
        Console.WriteLine(v);
        while (v != finish) {
            int t = (v | (v - 1)) + 1;  
            v = t | ((((t & -t) / (v & -v)) >> 1) - 1); 
            Console.WriteLine(v);
        }
    }
}

【讨论】:

    【解决方案2】:

    MBo 的答案是一个优雅的解决方案,如果您的总集合少于 64 个元素。如果您有更多元素,这里有一个迭代器,它将生成从一组 n 个元素中提取的 k 个元素的所有组合,以解决您的根本问题。

    using System;
    using System.Collections;
    using System.Collections.Generic;
    
    namespace CPHUtils
    {
        public class CombinationIterator : IEnumerable<int[]>
        {
            private int setSize;
            private int combinationSize;
            private int[] indices;
            private int indexToIncrement;
    
            /// <summary>
            /// Public constructor takes total set size and number of elements in the combinations to be returned
            /// </summary>
            /// <param name="combinationSize_"></param>
            public CombinationIterator(int combinationSize_, int setSize_)
            {
                if (combinationSize_ <= 0) throw new ArgumentException(string.Format("{0} ({1}) is <= 0", nameof(combinationSize_), combinationSize_));
                if (setSize_ <= 0) throw new ArgumentException(string.Format("{0} ({1}) is <= 0", nameof(setSize_), setSize_));
                if (combinationSize_ > setSize_) throw new ArgumentException(string.Format("{0} ({1}) is <= {2} ({3})",
                    nameof(combinationSize_), combinationSize_, nameof(setSize_), setSize_));
                this.combinationSize = combinationSize_;
                this.setSize = setSize_;
                // Create internal list of indices with one additional element to make comparison easier during iteration
                indices = new int[combinationSize + 1];
                for (int i = 0; i < combinationSize; i++)
                {
                    indices[i] = i;
                }
                indices[combinationSize] = setSize;
            }
    
            /// <summary>
            /// Returns subsequent arrays of <see cref="combinationSize"/> indices to enumerate all unique
            /// combinations of elements taken from a set of size <see cref="setSize"/>.<br/>
            /// Based on https://en.wikipedia.org/wiki/Combination "simpler faster way"<br/>
            /// There are many ways to enumerate k combinations. One way is to visit all the binary numbers less than 2n. 
            /// Choose those numbers having k nonzero bits, although this is very inefficient even for small n 
            /// (e.g. n = 20 would require visiting about one million numbers while the maximum number of allowed k combinations 
            /// is about 186 thousand for k = 10). The positions of these 1 bits in such a number is a specific k-combination of 
            /// the set { 1, …, n }.[8] Another simple, faster way is to track k index numbers of the elements selected, 
            /// starting with {0 .. k−1} (zero-based) or {1 .. k} (one-based) as the first allowed k-combination and then 
            /// repeatedly moving to the next allowed k-combination by incrementing the last index number if it is lower 
            /// than n-1 (zero-based) or n (one-based) or the last index number x that is less than the index number 
            /// following it minus one if such an index exists and resetting the index numbers after x to {x+1, x+2, …}.
            /// </summary>
            /// <returns>Subsequently returns an array of <see cref="combinationSize"/> int's such that each element
            /// is the index of an element in the original set, until all such unique combinations have been enumerated.</returns>
    
            public IEnumerator<int[]> GetEnumerator()
            {
                // On first call the array is already set up, can return it directly
                int[] result = new int[combinationSize];
                Array.Copy(indices, result, combinationSize);
                yield return result;
    
                indexToIncrement = combinationSize - 1;
    
                while (indexToIncrement >= 0)
                {
                    // Increment at this index until done there
                    while (indices[indexToIncrement] < (indices[indexToIncrement + 1] - 1))
                    {
                        indices[indexToIncrement]++;
                        result = new int[combinationSize];
                        Array.Copy(indices, result, combinationSize);
                        yield return result;
                    }
                    // Now start at next lower index
                    indexToIncrement--;
    
                    // Are we done?
                    if (indexToIncrement < 0) yield break;
    
                    // Room for further iterations here?
                    if (indices[indexToIncrement] < (setSize - combinationSize + indexToIncrement))
                    {
                        indices[indexToIncrement]++;
                        for (int i = indexToIncrement + 1; i < combinationSize; i++)
                        {
                            indices[i] = indices[i - 1] + 1;
                        }
                        result = new int[combinationSize];
                        Array.Copy(indices, result, combinationSize);
                        yield return result;
    
                        // If there is more room at the top, we need to start there again
                        if (indices[combinationSize - 1] < (setSize - 1))
                        {
                            indexToIncrement = combinationSize - 1;
                        }
                    }
                }
            }
    
            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }
        }
    }
    

    这就是你如何使用它:

    
            [Theory]
            [InlineData(1, 1, "A")]
            [InlineData(2, 2, "AB")]
            [InlineData(1, 4, "ABCD")]
            [InlineData(2, 4, "ABACADBCBDCD")]
            [InlineData(3, 4, "ABCABDACDBCD")]
            [InlineData(3, 3, "ABC")]
            public void genericCombinationIteratorUnitTest(int combinationSize, int setSize, string expectedValue)
            {
                string[] data = new string[setSize];
                for (int i = 0; i < setSize; i++)
                {
                    data[i] = ((char)(((int)'A') + i)).ToString();
                }
                CombinationIterator enumerator = new CombinationIterator(combinationSize, setSize);
                StringBuilder sb = new StringBuilder();
                foreach (int[] combination in enumerator)
                {
                    foreach (int ix in combination)
                    {
                        sb.Append(data[ix]);
                    }
                }
                string result = sb.ToString();
                Assert.Equal(expectedValue, result);
            }
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-06-22
      • 1970-01-01
      • 2019-04-19
      • 1970-01-01
      • 1970-01-01
      • 2020-05-25
      • 1970-01-01
      相关资源
      最近更新 更多