基于项目索引将数组分成N组的算法(应该是一些简单的东西)

3

我觉得这应该是非常简单和明显的事情,但我卡在这上面已经半个小时了,无法继续。

我所需要的只是将一个元素数组根据元素索引分成N组。

例如,我们有一个包含30个元素[e1,e2,... e30]的数组,需要将其分成N = 3组,如下所示:

group1: [e1, ..., e10]
group2: [e11, ..., e20]
group3: [e21, ..., e30]

对于N=3,我得到了下面这样的混乱代码(伪代码,为了说明我把乘法保留在0和1上):

for(i=0;i<array_size;i++) {
   if(i>=0*(array_size/3) && i<1*(array_size/3) {
      print "group1";
   } else if(i>=1*(array_size/3) && i<2*(array_size/3) {
      print "group2";
   } else if(i>=2*(array_size/3) && i<3*(array_size/3)
      print "group3";
   }
}

但是什么才是适当的通用解决方案呢?

谢谢。


这个问题中最重要的教训是:边界条件、短期(人为)截止日期和缺乏单元测试是致命的组合。 - Adam Liss
1
你想要做什么?是将数组分成三个较小的数组,还是打印30次“groupI”? - blabla999
8个回答

8
这个怎么样?
for(i=0;i<array_size;i++) {
  print "group" + (Math.floor(i/(array_size/N)) + 1)
}

谢谢,最短的一个且不需要额外的变量。 - serg
让array_size = 8,N = 3。由于整数运算得出8/3 = 2,因此“3”个组分别为{0,1},{2,3},{3,4},{6,7}。 - Adam Liss
是的,它应该看起来像这样:Math.floor(i/Math.ceil(array_size/N)) + 1 - serg
2
如果你跟我一样在索引值数组时,不要在结尾加上+1 :) - davertron
我觉得这个代码并没有将它很好地分成组。我已经做了一个demo. 我想把分组扩展成[3,3,2,2,2]而不是[2,2,3,2,3] - vsync

4

这里有一个小函数,它可以做你想要的 - 它预设你知道你需要创建的组数:

function arrayToGroups(source, groups) {  

                     //This is the array of groups to return:
                     var grouped = [];

                     //work out the size of the group
                     var groupSize = Math.ceil(source.length/groups);

                     //clone the source array so we can safely splice it (splicing modifies the array)
                     var queue = source.slice(0);

                     for (var r=0;r<groups;r++) {
                       //Grab the next groupful from the queue, and append it to the array of groups
                       grouped.push(queue.splice(0, groupSize));            
                     }       
             return grouped;
}

你可以这样使用它:

var herbs = ['basil', 'marjoram', 'aniseed', 'parsely', 'chives', 'sage', 'fennel', 'oregano', 'thyme', 'tarragon', 'rosemary'];

var herbGroups = arrayToGroups(herbs, 3);

这将返回:

herbGroups[0] = ['basil', 'marjoram', 'aniseed', 'parsely']
herbGroups[1] = ['chives', 'sage', 'fennel', 'oregano']
herbGroups[2] = ['thyme', 'tarragon', 'rosemary']

它不会对你传入的数组和数字进行任何合理性检查,但你可以很容易地添加这个功能。你还可以将其原型化为Javascript对象类型,这将在数组上提供一个方便的“toGroups”方法。


我正在寻找一种展开数组的方法,我认为你使用splice的方法非常巧妙。(实际用例是通过一个中间API传递一组坐标的数组,该API仅允许数组)。 - Neil
谢谢,尼尔!很高兴能帮忙 - 我完全忘记了这个。 - Ben Hull

2
我修改了Beejamin上面的函数并想分享一下。
function arrayToGroups($source, $pergroup) {  
    $grouped = array();
    $groupCount = ceil(count($source)/$pergroup);
    $queue = $source;
    for ($r=0; $r<$groupCount; $r++) {
        array_push($grouped, array_splice($queue, 0, $pergroup));    
    }       
    return $grouped;
}

这个问题询问每组应该有多少项,而不是总共有多少组。PHP。


2

使用向量语言可以使这个任务变得简单,选对工具也很重要。我只是想提供一种替代方法,让大家了解一下。

K语言中的解释版本(一种APL的后代):

split:{[values;n]    / define function split with two parameters
  enum:!n            / ! does enumerate from 0 through n exclusive, : is assign
  floor:_(#values)%n / 33 for this sample, % is divide, _ floor, # count
  cut:floor*enum     / 0 33 66 for this sample data, * multiplies atom * vector
  :cut _ values      / cut the values at the given cutpoints, yielding #cut lists
  }

values:1+!30           / generate values 1 through 30
n:3                    / how many groups to split into
groups:split[values;n] / set the groups

产生了预期的输出:
(1 2 3 4 5 6 7 8 9 10
 11 12 13 14 15 16 17 18 19 20
 21 22 23 24 25 26 27 28 29 30)

简短的K版本:

split:{((_(#x)%y)*!y)_ x}
groups:split[1+!30;3]

产生相同的输出:
(1 2 3 4 5 6 7 8 9 10
 11 12 13 14 15 16 17 18 19 20
 21 22 23 24 25 26 27 28 29 30)

+1 来源于原创性和评论,真希望我能给你 +10 来验证输出! - Adam Liss

1
 const int g = 3;                      // number of groups
 const int n = (array_size + g - 1)/g; // elements per group

 for (i=0,j=1; i<array_size; ++i) {
    if (i > j*n)
        ++j;
     printf("Group %d\n", j);
 }

1
int group[3][10];
int groupIndex = 0;
int itemIndex = 0;
for(i = 0; i < array_size; i++)
{
    group[groupIndex][itemIndex] = big_array[i];
    itemIndex++;
    if (itemIndex == 10)
    {
        itemIndex = 0;
        groupIndex++;   
    }
}

1

可能有无数种方法可以做到这一点。 我建议:对于每个组,创建一个基本指针和计数。

struct group {foo * ptr; size_t count };
group * pgroups = new group [ngroups];
size_t objects_per_group = array_size / ngroups;
for (unsigned u = 0; u < ngroups; ++u ) {
   group & g =  pgroups[u];
   size_t index = u * objects_per_group;
   g.ptr = & array [index];
   g.count = min (objects_per_group, array_size - index);  // last group may have less!
}
...`
for (unsigned u = 0; u < ngroups; ++u) {
   // group "g" is an array at pgroups[g].ptr, dimension pgroups[g].count
   group & g =  pgroups[u];
   // enumerate the group:
   for (unsigned v = 0; v < g.count; ++v) {
      fprintf (stdout, "group %u, item %u, %s\n",
         (unsigned) u, (unsigned) v, (const char *) g.ptr[v]->somestring);
}  }

delete[] pgroups;

0

我认为这个问题有点复杂;考虑到你只把组看作是一个一维的问题,你会对组的实际情况产生非常奇怪的看法。

首先,根据你处理的组素数和组合数量,这个问题是多维的。在数学中,这可以表示为n的n次方或n^n,可以转化为!n(n的阶乘)。

如果我有5个组排列成(1、2、3、4、5),然后我想将其表示为某些组或组合的阶乘表达式,那么组合就会变得更大。

组1x1 = 1,2,3,4,5
组2x1 = 12, 23, 45, 13, 14, 15, 21, 24, 25, 31, 32, 34, 35, 41, 42, 43, 45, 51, 52, 53, 54
因此,该策略创建了一个系统分支(非常简单)
12, 13, 14, 15
21, 22, 23, 24
31, 32, 34, 35
41, 42, 43, 45
51, 52, 53, 55
组1 + 2x2x1 = (1, 23, 45), (2, 13, 45), (3, 12, 45), (4, 12, 35), (1, 24, 35), (1, 25, 35), (1, 32, 45), (1, 34, 25), (1, 35, 24), ... 等等

当你开始添加阶乘集时,你会发现组合变得不那么容易创建一个数学参考来表达这些术语。当你进入一个基本集合> 3或4长度时,情况会变得更糟。

如果我理解你的问题:你想以通用术语表达一种算法,使你能够以编程方式创建分组策略?

这是一个复杂的集合;最好用微积分表示;作为集合论。否则,你所做的就是二维数组处理。

第一个数组表示分组策略; 第二个数组表示分组元素。

我认为这不是你被要求做的事情,因为在数学中,“GROUP”这个术语有一个非常特定的分配。你不应该使用“group”这个术语;而应该将其表示为一个集合;set1,set2,如果这是你正在做的事情。

Set1 包含 Set2 的元素;因此,这与集合和并集的数学处理方式相同。查找“Vin 图”和“Union”;除非您表示集合的因素,否则避免使用术语“group”。
http://en.wikipedia.org/wiki/Group_(mathematics)

我认为您试图表达的是已知集合或表格中的组;这在 wikipedia.org 的示例 D2 中有说明。

如果是这种情况,那么意味着您必须像解魔方一样看待问题;这会变得很复杂。

我正在使用 JavaScript 解决同样的问题;完成后我可能会发布它 ;)。这非常复杂。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接