【问题标题】:Count number of occurrences of each unique number: algorithm almost works计算每个唯一数字的出现次数:算法几乎可以工作
【发布时间】:2014-03-13 21:38:47
【问题描述】:

我正在尝试从 C 语言的开始逐步提高,并确保在继续之前我理解每一件小事。今天,我的目标是编写一个程序,该程序将接收一个整数列表(假设小于 50 个整数),并打印一个表格,其中一侧是唯一整数列表,另一侧是它出现的次数。我有一份我的函数的副本,它负责计算它出现的次数。

我的函数的快速摘要:接受 2 个指向数组的指针,以及要迭代多少个整数的一个整数。假设:我们正在检查某个数字 x 的重复。在数组中的某个地方,我们碰到了另一个 x。它增加 x 的计数并将 x 变为 0 以供以后使用。

样品试验 输入:1 2 1 2 2 1 2 输出:1 出现 3 次。 2 出现 4 次。

输入:1 2 3 1 2 3 1 2 3 输出:1 出现 3 次。 2 出现 3 次。 3 出现 3 次。

输入:1 2 1 3 1 2 3 输出:1 出现 3 次。 2 出现 2 次。 3 出现 1 次。

虽然该程序大部分时间都在工作,但我想确保它完全正常工作。因此,我的问题是最后一次审判。为什么 3 在适用于其他 2 个输入集时只被读取一次?

    void countOccurrences(int *list, int size, int *uniques){
for (int i = 0, t = 0; i < size; i++){
    int temp = list[i];
    if (temp == 0){                     //If the number was a repeat of another previous number
        continue;                       //skip over it and go to the next element in list
    }
    uniques[t] = 1;

    for (int j = i+1; j <= size; j++){      //this iterates through list for any repeats of temp
        if (temp == list[j]){           //All repeats of temp turn to 0
            uniques[i]++;
            list[j] = 0;
        }
    }
    t++;
}

}

【问题讨论】:

    标签: c arrays algorithm int counting


    【解决方案1】:

    这是因为,3 是最后一个数字,而您将出现次数重置为 1

    uniques[t] = 1;
    

    for 循环根本不运行,因为那是最后一个数字,你没有回头看数组。

    我会简单地写这个程序如下。给定列表的值> = 0

    for (int i = 0; i < size; i++){      //this iterates through list for any repeats of temp
                uniques[list[i]]++;
     }
    

    对于任何值的列表,使用hash table 数据结构

    【讨论】:

    • 实际上我花了一段时间才弄清楚有什么不同。虽然这个功能绝对是那么简洁和高效,但我不会使用你的功能,因为我将在你所说的条件之外运行。但是,我一定会保留它以备将来使用。谢谢!
    • 如果您在我所说的条件之外进行操作,请使用哈希表来计算重复出现次数
    【解决方案2】:

    我不会踩踏原始数据。

    高级视图:

    for each element
       if it apeared before
          increment it's count
       else
          record it's first occurence
    

    假设你需要计算一个 N 元素数组的内容,它不能包含超过 N 个不同的元素。表示计数的一种简单方法是有一个值和计数的数组,以及许多使用的条目(看到不同的值)。这将是大意:

    #define N ...
    
    struct {
             int value, cnt;
           } count[N];
    int entries = 0;
    

    您检查v 的值是否已经存在:

    for(k = 0; k < entries && count[k].value != v; k++)
        ;
    if(k == entries) {
        /* Not found */
        count[k].value = v;
        count[k].cnt   = 1;
        entries++;
    }
    else {
        /* Found it */
        count[k].value++;
    }
    

    只需用代码将其包装起来即可梳理您的数据数组...

    (是的,这非常低效;严重需要使用更智能/更快的结构来保持值)。

    【讨论】:

      【解决方案3】:

      这段代码有几个问题,我们可以通过一些更完整的测试来说明。这是一个简短的、独立的、可编译的(在 C99 中)示例(参见 SSCCE),其中包含一些测试和一些额外的诊断结果:

      #include <stdio.h>
      
      void printArray(char *name, int *list, int size) {
          printf ("%s = {",name);
          for (int i = 0; i < size; i++) {
              printf ("%d ",list[i]);
          }
          printf ("}\n");
      }
      
      void countOccurrences(int *list, int size, int *uniques, int *values) {
          for (int i = 0, t = 0; i < size; i++) {
              int temp = list[i];
              if (temp == 0) {                     
                  //If the number was a repeat of another previous number
                  continue;                       
                  //skip over it and go to the next element in list
              }
              uniques[t] = 1;
              values[t] = temp;
      
              for (int j = i+1; j <= size; j++) {      
                  //this iterates through list for any repeats of temp
                  if (temp == list[j]) {           
                      //All repeats of temp turn to 0
                      uniques[i]++;
                      list[j] = 0;
                  }
              }
          t++;
          }
      }
      
      void test(int *x, int size) {
          const int n = 10;
          int uniques[n],values[n];
          for (int i = 0; i < n; i++) {uniques[i] = 0; values[i] = -1; }
          countOccurrences (x,size,uniques,values);
          printArray ("uniques",uniques,sizeof(uniques)/sizeof(*uniques));
          printArray ("values ",values,sizeof(values)/sizeof(*uniques));
      }
      
      int main (int argc, char* argv[]) {
          int x1[] = {1, 2, 1, 2, 2, 1, 2};
          int x2[] = {1, 2, 3, 1, 2, 3, 1, 2, 3};
          int x3[] = {1, 2, 1, 3, 1, 2, 3};
          int x4[] = {3, 2, 1, 3, 1, 2, 3};
      
          test(x1,sizeof(x1)/sizeof(*x1));
          test(x2,sizeof(x2)/sizeof(*x2));
          test(x3,sizeof(x3)/sizeof(*x3));
          test(x4,sizeof(x4)/sizeof(*x4));
          return 0;
      }
      

      (编辑感谢@Matt McNabb 的建议,将通用代码重构为test() 函数)

      ... 其输出为:

      uniques = {3 4 0 0 0 0 0 0 0 0 }
      values  = {1 2 -1 -1 -1 -1 -1 -1 -1 -1 }
      uniques = {4 3 3 0 0 0 0 0 0 0 }
      values  = {1 2 3 -1 -1 -1 -1 -1 -1 -1 }
      uniques = {4 2 1 1 0 0 0 0 0 0 }
      values  = {1 2 3 -1 -1 -1 -1 -1 -1 -1 }
      uniques = {3 2 3 0 0 0 0 0 0 0 }
      values  = {3 2 1 -1 -1 -1 -1 -1 -1 -1 }
      

      第一个测试会为您提供预期的输出。第二个测试显示列表中的第一项有额外的计数。这可以通过更改来解决:

      for (int j = i+1; j <= size; j++){
      

      for (int j = i+1; j < size; j++){
      

      ...因为代码正在计算数据末尾之外的一个空格。修复了这个错误的输出是:

      uniques = {3 4 0 0 0 0 0 0 0 0 }
      values  = {1 2 -1 -1 -1 -1 -1 -1 -1 -1 }
      uniques = {3 3 3 0 0 0 0 0 0 0 }
      values  = {1 2 3 -1 -1 -1 -1 -1 -1 -1 }
      uniques = {3 2 1 1 0 0 0 0 0 0 }
      values  = {1 2 3 -1 -1 -1 -1 -1 -1 -1 }
      uniques = {3 2 2 0 0 0 0 0 0 0 }
      values  = {3 2 1 -1 -1 -1 -1 -1 -1 -1 }
      

      第三和第四个测试结果更难解释,因为预期的输出应该是什么并不那么明显。计数功能似乎旨在按照在list 中找到这些数字的顺序报告唯一数字的计数。然而,在第三次测试中,“3”的第一次出现在列表中的第四项。改变:

      uniques[i]++;
      

      uniques[t]++;
      

      ... 表示计数作为计数列表中的tth 项输出,给出输出:

      uniques = {3 4 0 0 0 0 0 0 0 0 }
      values  = {1 2 -1 -1 -1 -1 -1 -1 -1 -1 }
      uniques = {3 3 3 0 0 0 0 0 0 0 }
      values  = {1 2 3 -1 -1 -1 -1 -1 -1 -1 }
      uniques = {3 2 2 0 0 0 0 0 0 0 }
      values  = {1 2 3 -1 -1 -1 -1 -1 -1 -1 }
      uniques = {3 2 2 0 0 0 0 0 0 0 }
      values  = {3 2 1 -1 -1 -1 -1 -1 -1 -1 }
      

      这个输出现在是正确的,但是如果没有我添加到代码中的values 数组,很难解释在uniques 中找到的计数。请注意,在最后一个测试用例中,第一个计数是 list 中的 3s 的数量,而不是 1s 的数量。

      最后,将参数修改为函数通常是不好的做法。在 C 中这样做是必要的,因为您不能从函数返回数组,但是修改 uniquesvalues 指向的数组通常是可以容忍的,因为它们可以明确地用于从函数向外返回结果。但是,修改用于向函数提供输入数据的参数,就像 countOccurrences()list 所做的那样通常是不明智的,因为这意味着使用 countOccurrences() 的代码必须在将指针传递给之前复制 list如果它还想将list 的原始内容用于其他目的,则该列表为countOccurrences()

      如果我们知道要计算的整数都小于或等于uniques 数组的大小,那么@Saravana Kumar 建议的函数运行起来更快,更容易纠正:

      // Requirements: 
      // uniques initially contains all zeros
      // no integer in list is less than zero or greater than sizeof(uniques)/sizeof(int)-1
      //
      void countOccurrences2 (int *list, int size; int *uniques) {
          for (int i = 0; i < size; i++) {      
              uniques[list[i]]++;
          }
      }
      

      【讨论】:

      • 这有助于澄清很多事情。非常感谢您,不仅解释了这些问题,还解释了其他人的答案。 SSCCE 非常有用;我想我会尝试将它作为构建这些东西的默认指南。再次感谢!
      • 使用sizeof(x) / sizeof(*x) 而不是使用“魔法令牌”可能有助于避免大小错误。我也会使用宏来提高代码的可读性,尽管这只是个人风格。
      猜你喜欢
      • 1970-01-01
      • 2011-05-12
      • 2012-10-25
      • 1970-01-01
      • 1970-01-01
      • 2020-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多