有多个相关问题,但我正在寻找针对我的案例的解决方案。有一个(通常)14个整数的数组,每个整数在1到34的范围内。如何快速判断特定静态列表中的每个int是否在此数组中至少出现一次?
作为参考,我目前正在使用这段代码,它尽可能地编写成与规范类似的代码,所以它当然可以大大改进:
if (array.Count < 13) {
return;
}
var required = new int[] {
0*9 + 1,
0*9 + 9,
1*9 + 1,
1*9 + 9,
2*9 + 1,
2*9 + 9,
3*9 + 1,
3*9 + 2,
3*9 + 3,
3*9 + 4,
3*9 + 5,
3*9 + 6,
3*9 + 7,
};
IsThirteenOrphans = !required.Except (array).Any ();
所需列表不是动态的,即在运行时它始终是相同的。使用Linq是可选的,主要方面是性能。
修改
更新:我也对排序输入数组的解决方案感兴趣。
答案 0 :(得分:1)
创意1
如果您需要与几个required
列表进行比较,那么您可以对输入列表进行排序,然后通过迭代进行比较。但排序当然不是太快,但也不是太慢。但是,如果您与几个必需的列表进行比较,排序的开销可能会很快摊销。
一旦数组被排序,比较是微不足道的:
for(int i = 0; i < 14; i++)
if(arr[i] != required[i]) return false;
return true;
创意2
或者,如果14个整数是不同/唯一的,您可以简单地将required
设为HashSet并执行
input.Count(i => required.Contains(i)) == 14
但如果没有实际测试它,如果这比排序更快,我不知道。
创意3
计算一个快速哈希值,该哈希值在14个整数的排列下是不变的,并将其与require
的已知值进行比较。只有哈希匹配才能进行更昂贵的比较。
//Prepare/precalculated
int[] hashes = new int[34];
Random random = new Random();
for(int i = 0; i < 34; i++)
hashes[i] = random.NextInt();
//On each list
int HashInts(int[] ints)
{
int result = 0;
foreach(int i in ints)
result += hashes[i - 1];
return result;
}
明智地选择hashes
的值可能会有所改善,但随机值应该没问题。
创意4
创建直方图:
int[] CreateHistogram(int[] ints)
{
int[] counts = new int[34];
foreach(int i in ints)
{
counts[i - 1]++;
}
return counts;
}
如果性能非常重要,您可以通过重用现有阵列来避免创建阵列。
答案 1 :(得分:1)
如果你想要快速的方式,你不应该使用linq,如果给定的列表项全部都在下面35,你可以删除if (lst[i] < 35)
波纹管回答遍历列表最多一次,行为类似于counting sort
:
public bool FindExactMatch(int[] array, List<int> lst)
{
bool[] a34 = new bool[35];
foreach(var item in array)
{
a34[item] = true;
}
int exact = 0;
for (int i = 0; i < lst.Count; i++)
{
if (a34[lst[i]])
{
exact++;
if (exact == array.Length) return true;
a34[lst[i]] = false;
}
}
return false;
}
对于排序列表,如果列表大小很大,你可以lst.BinarySearch(array[i])
最多需要14 * log(n)* c1,而且我认为它也足够有效,如果你实现它可能会变得更快,我没有使用我自己的实现测试二进制搜索,但是linq中的Min,Max,Sort比你自己的(好的)实现慢(4到10次)。
如果排序列表大小很小,我更喜欢使用上面的算法,因为常量c1
在上述算法中很小,在二进制搜索算法中可能更大。
答案 2 :(得分:1)
如果你有一个34位以上的积分类型和C样式位操作,那么你可以从变量列表中计算这样一个整数V(如果列表是v [0],v [1],...那么V是(1
答案 3 :(得分:1)
一种可能性是更改存储数据的方式。由于可能值的范围限制为1-34,而不是存储数字列表,您可以存储每个数字的计数:
int[] counts = new int[34];
如果你的列表有一个1和两个3,那么计数[0] == 1&amp;&amp; count [2] = 2(你可以选择使用基于1的索引,如果这会使事情变得更快(减少更少)。)
现在要评估列表中的每个int至少出现一次,您只需按顺序为每个x索引数组,并验证所有计数[x]&gt; 0.将数据从计数表单转换为列表表单会产生相关的开销,但如果您经常需要查看列表表单中的数据,则这只是一个问题。这种存储格式的另一个优点是添加/删除计数永远不会涉及多个数组元素;在列表表单中,删除列表中间的元素需要多个元素的副本。
答案 4 :(得分:1)
这看起来像是按位运算的一个很好的候选者,因为所需的值是不同的,静态的,在1到34之间。而不是将所需的值保存为数组,将其保存为const ulong。在要检查的数组中,创建一个新的ulong填充左移每个值并按位或。
<h1>new</h1>
<%= form_for @comment do |form| %>
<div class="form-group">
<p>
<%= form.label :fav_drink %>
<%= form.text_field :fav_drink %>
<%= form.hidden_field :subscriber_id %>
</p>
<%= form.submit "Send", class: "btn btn-primary" %>
</div>
<% end %>
答案 5 :(得分:0)
您可以轻松遍历这些项目并找出是否有任何项目丢失。通过你的例子我明白你只是想知道数组中是否有任何需要的项目。所以你可以写
bool notContains = false;
foreach (var iddd in required)
{
foreach (var ar in array)
{
if (iddd == ar) notContains=true;
}
if (notContains == false) break;
}
这比你的方法要快得多。添加计时器,查看以下代码。你的方法花了5毫秒,但新的花了0毫秒
System.Diagnostics.Stopwatch aTimer = new System.Diagnostics.Stopwatch();
aTimer.Start();
var IsThirteenOrphans = !required.Except(array).Any();
aTimer.Stop();
System.Diagnostics.Stopwatch bTimer = new System.Diagnostics.Stopwatch();
bTimer.Start();
bool notContains = false;
foreach (var iddd in required)
{
foreach (var ar in array)
{
if (iddd == ar) notContains=true;
}
if (notContains == false) break;
}
bTimer.Stop();
答案 6 :(得分:0)
修改强> 所以我理解你的问题,可能有一些很复杂的解决方案。另一个问题是,它的表现有多好。
static void Main(string[] args)
{
var required = new int[]
{
0*9 + 1,
0*9 + 9,
1*9 + 1,
1*9 + 9,
2*9 + 1,
2*9 + 9,
3*9 + 1,
3*9 + 2,
3*9 + 3,
3*9 + 4,
3*9 + 5,
3*9 + 6,
3*9 + 7,
};
precomputed = required.Select((x, i) => new { Value = x, Offset = (UInt16)(1 << i) }).ToDictionary(x => x.Value, x => x.Offset);
for (int i = 0; i < required.Length; i++)
{
precomputedResult |= (UInt16)(1 << i);
}
int[] array = new int[34]; // your array goes here..
Console.WriteLine(ContainsList(array));
Console.ReadKey();
}
// precompute dictionary
private static Dictionary<int, UInt16> precomputed;
// precomputed result
private static UInt16 precomputedResult = 0;
public static bool ContainsList(int[] values)
{
UInt16 result = 0;
for (int i = 0; i < values.Length; i++)
{
UInt16 v;
if (precomputed.TryGetValue(values[i], out v))
result |= v;
}
return result == precomputedResult;
}