- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我有一个包含这些值的列表:{1、2、3、4、5、6、7}。我希望能够检索三个的独特组合。结果应该是这样的:
{1,2,3}
{1,2,4}
{1,2,5}
{1,2,6}
{1,2,7}
{2,3,4}
{2,3,5}
{2,3,6}
{2,3,7}
{3,4,5}
{3,4,6}
{3,4,7}
{3,4,1}
{4,5,6}
{4,5,7}
{4,5,1}
{4,5,2}
{5,6,7}
{5,6,1}
{5,6,2}
{5,6,3}
我已经有 2 个 for 循环可以做到这一点:
for (int first = 0; first < test.Count - 2; first++)
{
int second = first + 1;
for (int offset = 1; offset < test.Count; offset++)
{
int third = (second + offset)%test.Count;
if(Math.Abs(first - third) < 2)
continue;
List<int> temp = new List<int>();
temp .Add(test[first]);
temp .Add(test[second]);
temp .Add(test[third]);
result.Add(temp );
}
}
但是由于我正在学习 LINQ,我想知道是否有更聪明的方法来做到这一点?
最佳答案
更新:我用这个问题作为开始 here 的一系列文章的主题;我将在该系列中介绍两种略有不同的算法。感谢您提出很好的问题!
到目前为止发布的两个解决方案是正确的,但对于数字变大的情况效率低下。目前贴出的解决方案使用算法:首先枚举所有的可能性:
{1, 1, 1 }
{1, 1, 2 },
{1, 1, 3 },
...
{7, 7, 7}
在这样做的同时,过滤掉第二个不大于第一个,第三个不大于第二个的任何地方。这执行 7 x 7 x 7 过滤操作,这不是很多,但是如果你试图从三十个元素中获得十个元素的排列,那就是 30 x 30 x 30 x 30 x 30 x 30 x 30 x 30 x 30 x 30,相当多。你可以做得更好。
我会按如下方式解决这个问题。首先,生成一个高效的不可变集数据结构。让我非常清楚什么是不可变集,因为您可能不熟悉它们。您通常将集合视为您添加项目和从中删除项目的东西。不可变集有一个Add
操作,但它不会改变集;它会返回一个包含添加项的新 集合。删除相同。
这是一个不可变集合的实现,其中元素是从 0 到 31 的整数:
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System;
// A super-cheap immutable set of integers from 0 to 31 ;
// just a convenient wrapper around bit operations on an int.
internal struct BitSet : IEnumerable<int>
{
public static BitSet Empty { get { return default(BitSet); } }
private readonly int bits;
private BitSet(int bits) { this.bits = bits; }
public bool Contains(int item)
{
Debug.Assert(0 <= item && item <= 31);
return (bits & (1 << item)) != 0;
}
public BitSet Add(int item)
{
Debug.Assert(0 <= item && item <= 31);
return new BitSet(this.bits | (1 << item));
}
public BitSet Remove(int item)
{
Debug.Assert(0 <= item && item <= 31);
return new BitSet(this.bits & ~(1 << item));
}
IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
public IEnumerator<int> GetEnumerator()
{
for(int item = 0; item < 32; ++item)
if (this.Contains(item))
yield return item;
}
public override string ToString()
{
return string.Join(",", this);
}
}
仔细阅读此代码以了解其工作原理。再次强调,永远记住向这个集合添加一个元素不会改变集合。它会生成包含已添加项目的新集合。
好的,现在我们已经知道了,让我们考虑一个更有效的算法来生成你的排列。
我们将递归地解决这个问题。递归解决方案始终具有相同的结构:
让我们从琐碎的问题开始。
假设您有一个集合,并且您希望从中选择零 个项目。答案很明确:元素为零的排列只有一种可能,那就是空集。
假设您有一个包含 n 个元素的集合,并且您想要选择多于 n 个元素。显然没有解决方案,甚至没有空集。
我们现在已经处理了集合为空或所选元素的数量多于元素总数的情况,因此我们必须从至少有一个事物的集合中选择至少一个事物。
在可能的排列中,有些排列中有第一个元素,有些则没有。找到所有包含第一个元素的元素并生成它们。我们通过递归选择集合中少一个第一个元素的元素来做到这一点。
那些没有的元素包含第一个元素,我们通过枚举没有第一个元素的集合的排列来找到它们。
static class Extensions
{
public static IEnumerable<BitSet> Choose(this BitSet b, int choose)
{
if (choose < 0) throw new InvalidOperationException();
if (choose == 0)
{
// Choosing zero elements from any set gives the empty set.
yield return BitSet.Empty;
}
else if (b.Count() >= choose)
{
// We are choosing at least one element from a set that has
// a first element. Get the first element, and the set
// lacking the first element.
int first = b.First();
BitSet rest = b.Remove(first);
// These are the permutations that contain the first element:
foreach(BitSet r in rest.Choose(choose-1))
yield return r.Add(first);
// These are the permutations that do not contain the first element:
foreach(BitSet r in rest.Choose(choose))
yield return r;
}
}
}
现在我们可以问您需要回答的问题:
class Program
{
static void Main()
{
BitSet b = BitSet.Empty.Add(1).Add(2).Add(3).Add(4).Add(5).Add(6).Add(7);
foreach(BitSet result in b.Choose(3))
Console.WriteLine(result);
}
}
我们完成了。我们只生成了实际需要的序列。 (虽然我们已经做了很多集合操作来达到这个目的,但是集合操作很便宜。)这里的重点是理解这个算法是如何工作的是非常有启发性的。不可变结构的递归编程是许多专业程序员的工具箱中没有的强大工具。
关于c# - 使用 LINQ 获取所有可能的不同三元组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26316211/
降本手段一招鲜,增效方法吃遍天; 01 互联网行业里; 降本策略千奇百怪,手段却出奇一致;增效方法五花八门,手段更是花里胡哨; 对于企业来说;
有什么方法可以使用 angularjs 中的部分进行代码分组吗? 原因 --- 我的 Controller 包含太多代码。该 Controller 包含了多个方法和大量功能的代码,降低了代码的可读性。
不幸的是,我的数据库的数据模型必须改变,所以我正在寻找最轻松的方式来迁移我的数据。 此时情况如何: create table cargo{ id serial primary key, per
在 QTextEdit 对象中,假设我想知道字符在鼠标光标下的位置。 我会写... void MyQTextEditObject::mousePressEvent(QMouseEvent* mouse
是否可以在 C++ 中返回一个 return 语句或做一些具有类似功能的事情? 例如,如果代码中有几个函数将指针作为输入,并且每个函数都检查指针是否为 nullptr,这将很方便。如果它是一个 nul
我的 PC 上有一个控制台应用程序,它是 signalR 服务器。 我有一个 html 页面,它是互联网上的 signalR 客户端。但我尝试连接服务器,但我有一个错误的请求 400 错误。如果服务器
我想将应用程序作为后台进程运行。当点击应用程序图标时,它不会显示任何 View ,只会启动后台进程。 最佳答案 对于 iOS 这是不可能的,但是对于 android,react native 有 he
我知道有(昂贵的)框架可以让你在 VS C# 中编写 android 应用程序并将其编译为 android apk。 我也知道,可以在 VS 中编写 Java 应用程序(link)。 是否有可能,甚至
我在做: can :manage, :all if user.role == 'admin' can :approve, Anuncio do |anuncio| anuncio.try(:apr
我是一名优秀的程序员,十分优秀!