如果给我一堆组合,每个组合都有k-1个元素,如何生成大小为k个元素的列表?

时间:2018-09-25 05:56:49

标签: java recursion combinations hashset

从某种意义上说,这与从包含k + 1个元素的数组中生成大小为k的子集的问题相反。

例如,如果有人给我{a,b},{a,c},{b,c},{a,e},{b,e},{a,f}对,我需要一种算法,该算法将告诉我三胞胎{a,b,c}和(a,b,e}在给定给我的对中完全成对组合。我需要将示例中的对/三胞胎概括为案例k / k + 1

我的直觉是,将有一个文档齐全且有效的算法可以解决我的问题。可悲的是,搜索互联网并没有帮助获得它。已经在stackoverflow中发布的问题不能解决此问题。因此,我不得不发布此问题以找到我的解决方案。

2 个答案:

答案 0 :(得分:0)

我对此并不熟悉,并且您没有要求使用特定的语言,因此我编写了一种C#算法,可以完成您所要求的并匹配提供的测试值。它没有太多实际的错误检查。我有一个.Net小提琴,您可以运行它来在Web浏览器中查看结果。 https://dotnetfiddle.net/ErwTeg

通过将数组(或类似容器)的数组转换为字典,并将每个唯一值作为键,而每个键的值就是在该键的任何列表中找到的每个值,则可以正常工作。从您的示例中,a得到{b,c,e,f}(我们称他们为朋友,而这就是GetFriends函数的作用)

AreFriendsWithEachother函数指示所有传递的值是否与其他所有值都是朋友。

然后,将朋友列表的结果馈送到MakeTeam函数,该函数通过枚举键具有的每个朋友并尝试对每个size长度进行排列来组成给定size的团队这些。例如,在原始示例中,a具有{{a,b,c},{a,b,e},{a,b,f},{a,c,b},{a,c,e},{a,c,f},{a,e,b},{a,e,c},{a,e,f},{a,f,b},{a,f,c},{a,f,e}}的朋友排列。在这些中,我们通过检查我们先前创建的朋友列表来确保所有三个值都是朋友。如果排列中的所有值都是朋友,则将其添加到结果缓存中。然后将针对所有重复集剔除结果。在C#中,这可以通过使用HashSet来处理,该方法仅添加列表中尚未包含的项目。

MakeTeam函数看起来很糟糕,因为它包含运行时变量数量的循环(通常由foreach可视化)。我在枚举器中上下滚动并自己模拟foreach循环。

我提供了MakeTeamOf3MakeTeamOf4的版本,这些版本显示了静态循环结构,当您提前知道k的值时,就很容易修改它们。

此处提供了相同的代码

using System;
using System.Collections.Generic;
using System.Linq;

namespace kfromkm1 // k elements from k minus 1
{
    public class Program
    {
        static readonly string[][] pairs =
        {
            new string[] { "a", "b" },
            new string[] { "a", "c" },
            new string[] { "b", "c" },
            new string[] { "a", "e" },
            new string[] { "b", "e" },
            new string[] { "a", "f" }
        };

        static readonly string[][] pairsExpectedResult =
        {
            new string[] { "a", "b", "c" },
            new string[] { "a", "b", "e" }
        };

        static readonly string[][] triplets =
        {
            new string[] { "a", "b", "c" },
            new string[] { "a", "b", "d" },
            new string[] { "a", "c", "d" },
            new string[] { "b", "c", "d" },
            new string[] { "b", "c", "e" }
        };

        static readonly string[][] tripletsExpectedResults =
        {
            new string[] { "a", "b", "c", "d" }
        };

        public static void Main(string[] args)
        {
            Dictionary<string, HashSet<string>> friendsList = GetFriends(pairs);
            Dump(nameof(pairs), pairs);
            Console.WriteLine();
            Dump(nameof(pairsExpectedResult), pairsExpectedResult);
            Console.WriteLine();
            HashSet<HashSet<string>> teams = MakeTeams(friendsList, 3);
            Dump(nameof(teams), teams);

            Console.WriteLine();

            friendsList = GetFriends(triplets);
            Dump(nameof(triplets), triplets);
            Console.WriteLine();
            Dump(nameof(tripletsExpectedResults), tripletsExpectedResults);
            Console.WriteLine();
            teams = MakeTeams(friendsList, 4);
            Dump(nameof(teams), teams);

            Console.ReadLine();
        }

        // helper function to display results
        static void Dump<T>(string name, IEnumerable<IEnumerable<T>> values)
        {
            Console.WriteLine($"{name} =");
            int line = 0;
            bool notfirst;
            foreach (IEnumerable<T> layer in values)
            {
                Console.Write($"{line}: {{");
                notfirst = false;
                foreach (T value in layer)
                {
                    if (notfirst)
                        Console.Write($", {value}");
                    else
                    {
                        Console.Write(value);
                        notfirst = true;
                    }
                }
                Console.WriteLine("}");
                line++;
            }
        }

        // items are friends if they show up in a set (pair in the example) together
        // list can be a list of lists, array of arrays, list of arrays, etc
        // {a, b} means a and b are friends
        // {a, b, c} means a is friends with b and c, b is friends with a and c, c is friends with a and b
        static Dictionary<T, HashSet<T>> GetFriends<T>(IEnumerable<IEnumerable<T>> list) where T : IEquatable<T>
        {
            Dictionary<T, HashSet<T>> result = new Dictionary<T, HashSet<T>>();
            foreach (IEnumerable<T> set in list) // one set at a time
            {
                foreach (T current in set) // enumerate the set from front to back
                {
                    foreach (T other in set) // enumerate the set with a second pointer to compare every item
                    {
                        if (!current.Equals(other)) // ignore self
                        {
                            if (!result.ContainsKey(current)) // initialize this item's result hashset
                                result[current] = new HashSet<T>();
                            result[current].Add(other); // add friend (hashset will ignore duplicates)
                        }
                    }
                }
            }
            return result;
        }

        // indicates whether or not all items are friends
        static bool AreFriendsWithEachother<T>(Dictionary<T, HashSet<T>> friendsList, IEnumerable<T> values)
        {
            if (friendsList == null) // no list = no results
                throw new ArgumentNullException(nameof(friendsList));
            foreach (T first in values)
            {
                if (!friendsList.ContainsKey(first)) // not on list, has no friends
                    return false;
                foreach (T other in values)
                {
                    if (!friendsList[first].Contains(other) && !first.Equals(other)) // false if even one doesn't match, don't count self as non-friend for computational ease
                        return false;
                }
            }
            return true; // all matched so true
        }

        // size represents how many items should be in each team
        static HashSet<HashSet<T>> MakeTeams<T>(Dictionary<T, HashSet<T>> friendsList, int size) where T : IEquatable<T>
        {
            if (friendsList == null) // no list = no results
                throw new ArgumentNullException(nameof(friendsList));
            if (size < 2)
                throw new ArgumentOutOfRangeException(nameof(size), size, "Size should be greater than 2");
            HashSet<HashSet<T>> result = new HashSet<HashSet<T>>(HashSet<T>.CreateSetComparer());
            T[] values = new T[size];
            IEnumerator<T>[] enumerators = new IEnumerator<T>[size - 1]; // gotta cache our own enumerators with a variable number of "foreach" layers
            int layer;
            bool moveNext;

            foreach (T key in friendsList.Keys) // this is a mess because it's a runtime variable number of copies of enumerators running over the same list
            {
                values[0] = key;
                for (int index = 0; index < size - 1; index++)
                    enumerators[index] = friendsList[key].GetEnumerator();
                moveNext = true;
                layer = 0;
                while (moveNext)
                {
                    while (layer < size - 1 && moveNext)
                    {
                        if (enumerators[layer].MoveNext())
                            layer++;
                        else
                        {
                            if (layer == 0)
                                moveNext = false;
                            else
                            {
                                enumerators[layer].Reset();
                                layer--;
                            }
                        }
                    }
                    for (int index = 1; index < size; index++)
                        values[index] = enumerators[index - 1].Current;
                    if (values.Distinct().Count() == size && AreFriendsWithEachother(friendsList, values))
                        result.Add(new HashSet<T>(values));
                    layer--;
                }
            }

            return result;
        }

        // provided as an example
        static HashSet<HashSet<T>> MakeTeamsOf3<T>(Dictionary<T, HashSet<T>> friendsList) where T : IEquatable<T>
        {
            if (friendsList == null) // no list = no results
                throw new ArgumentNullException(nameof(friendsList));
            HashSet<HashSet<T>> result = new HashSet<HashSet<T>>(HashSet<T>.CreateSetComparer());
            T[] values;

            foreach (T key in friendsList.Keys) // start with every key
            {
                foreach (T first in friendsList[key])
                {
                    foreach (T second in friendsList[key])
                    {
                        values = new T[] { key, first, second };
                        if (values.Distinct().Count() == 3 && AreFriendsWithEachother(friendsList, values)) // there's no duplicates and they are friends
                            result.Add(new HashSet<T>(values));
                    }
                }
            }

            return result;
        }

        // provided as an example
        static HashSet<HashSet<T>> MakeTeamsOf4<T>(Dictionary<T, HashSet<T>> friendsList) where T : IEquatable<T>
        {
            if (friendsList == null) // no list = no results
                throw new ArgumentNullException(nameof(friendsList));
            HashSet<HashSet<T>> result = new HashSet<HashSet<T>>(HashSet<T>.CreateSetComparer());
            T[] values;

            foreach (T key in friendsList.Keys) // start with every key
            {
                foreach (T first in friendsList[key])
                {
                    foreach (T second in friendsList[key])
                    {
                        foreach (T third in friendsList[key])
                        {
                            values = new T[] { key, first, second, third };
                            if (values.Distinct().Count() == 4 && AreFriendsWithEachother(friendsList, values)) // there's no duplicates and they are friends
                                result.Add(new HashSet<T>(values));
                        }
                    }
                }
            }
            return result;
        }
    }
}

答案 1 :(得分:0)

生成SetOfkNbrdElementCombinations的函数 //生成k值大于2(成对)的输出

  Take SetOfkNbrdElementCombinations as an input 
  //Example - {{a,b},{b,c},...} : here k is 2 (though variable name will retain the letter k); elements are a,b,c,..; sets {a,b}, {b,c} are 2-numbered combinations of elements

  Take nextSize as an input 
  //nextSize should be bigger than the k in the input SetOfkNbrdElementCombinations by 1. 
  //For example above where k is 2, nextSize would be 3

  //Logic: 
  Comb(SetOfkNbrdElementCombinations={S1,S2,...Sn},nextSize) = {S1,Comb({SetOfkNbrdElementCombinations-a1},nextSize-l)}

  //The recursive algorithm specified in the line above generates sets containing unique nextSize numbered combinations of the combinations in SetOfkNbrdElementCombinations
  //Code that implements the algorithm is available at Rosetta code
  //In our example it would generate {{{a,b},{b,c},{b,e}},{{a,b},{b,c},{a,c}},...} (triplets of pairs)


  //My logic to generate nextSize numbered combinations of elements is below
  // Example of my output, based on the example input above, would be {{a,b,c},{a,c,e},...}

  Intitialize oputSetOfkNbrdElementCombinations to empty

  For each nextSize sized combination of combinations generated above 
      Join the contained combinations in a union set
      If the nbr of elements in the union is nextSize, add the union set to oputSetOfkNbrdElementCombinations

  Output oputSetOfkNbrdElementCombinations

这是该算法的Java实现。您可以在https://ide.geeksforgeeks.org/

上复制,粘贴和运行
/* This program takes k sized element combinations and generates the k+1 sized element combinations
that are possible.  

For example if the program is given {a,b,c}, {a,b,d}, {a,c,d}, {b,c,d}, {b,c,e} 
which are 3 sized combinations, it will identify {a,b,c,d}  the  
4 sized combination that has all the 3 sized combinations of its elements covered
in what were provided to the program

The program can scale to higher values of k.  
The program uses only the hashset data structure
*/

//AUTHOR: Suri Chitti

import java.util.*;
public class uppOrdCombsFromCombs {


 //sample CSV strings...let us pretend they came from a file
 //This is a sample of input to the program
 static String[] csvStrings = new String[] {
                "a,b,c",
                "a,b,d",
                "a,c,d",
                "b,c,d",
                "b,c,e"
        };

/*  //Another sample CSV strings...let us pretend they came from a file
 //This is another sample of input to the program
 static String[] csvStrings = new String[] {
                "a,b",
                "b,c",
                "a,c",
                "c,e",
                "a,e"
        };      
      */  /////USE ONLY ONE SAMPLE

  //Before we can generate a k+1 sized combination of elements from a bunch
  //of k sized combinations we need to obtain groups containing k+1 number of
  // k sized combinations
  //The method below, called SetOfNxtSizeNbrdkElementCombinationsets will do it for us
  //It takes a bunch of k sized combinations called the parameter hsSetOfkNbrdCombinationsetsPrm
  //which is a hashset.
  //It also takes k+1 as input called the parameter nextSize which is an integer
  //Outputs k+1 sized groups of k sized element combinations as a variable called hsSetOfNxtSizeNbrdCombinationsets
  //which is hashset

  static HashSet SetOfNxtSizeNbrdCombinationsets(HashSet hsSetOfkNbrdCombinationsetsPrm, Integer nextSize){

      HashSet hsSetOfNxtSizeNbrdCombinationsets = new HashSet<>();//is it better to have nested <HashSet> tokens in this declaration?
      HashSet hsRecursor = new HashSet<>(hsSetOfkNbrdCombinationsetsPrm);

      Iterator <HashSet> loopIterator1 = hsSetOfkNbrdCombinationsetsPrm.iterator();
      while (loopIterator1.hasNext()) {

          HashSet hsName = loopIterator1.next(); 

          if(nextSize == 1){
            hsSetOfNxtSizeNbrdCombinationsets.add(hsName);

          }

          else {

            HashSet hsConc1 = new HashSet<>();
            hsRecursor.remove(hsName);

            hsConc1 = SetOfNxtSizeNbrdCombinationsets(hsRecursor,nextSize-1);

            Iterator <HashSet> loopIterator2 = hsConc1.iterator();

            while (loopIterator2.hasNext()) {

                HashSet hsConc2 = new HashSet<>();
                HashSet hsConc3 = new HashSet<>();

                hsConc2 = loopIterator2.next(); 

                Iterator <HashSet> loopIterator3 = hsConc2.iterator();

                Object obj = loopIterator3.next();

                if (String.class.isInstance(obj)) {
                    hsConc3.add(hsName);
                    hsConc3.add(hsConc2);

                }
                else {
                    loopIterator3 = hsConc2.iterator();
                    hsConc3.add(hsName);
                    while (loopIterator3.hasNext()) {
                    hsConc3.add(loopIterator3.next());
                    }
                } 
                 hsSetOfNxtSizeNbrdCombinationsets.add(hsConc3);
            }
           }

        }
      return hsSetOfNxtSizeNbrdCombinationsets;
  }

  //The method below takes the k+1 sized groupings of k sized element combinations
  //generated by the method above and  generates all possible K+1 sized combinations of 
  //elements contained in them
  //Name of the method is SetOfkNbrdCombinationsets 
  //It takes the k+1 sized groupings in a parameter called hsSetOfNxtSizeNbrdCombinationsetsPrm which is a HashSet
  //It takes the value k+1 as a parameter called nextSize which is an Integer
  //It returns k+1 sized combinations as a variable called hsSetOfkNbrdCombinationsets which is a HashSet
  //This is the intended output of the whole program

  static HashSet SetOfkNbrdCombinationsets(HashSet hsSetOfNxtSizeNbrdCombinationsetsPrm, Integer nextSize){
      HashSet hsSetOfkNbrdCombinationsets = new HashSet<>();
      HashSet hsMember = new HashSet<>();

      Iterator <HashSet> loopIteratorOverParam = hsSetOfNxtSizeNbrdCombinationsetsPrm.iterator();
      while (loopIteratorOverParam.hasNext()) {
      hsMember = loopIteratorOverParam.next();


      HashSet hsInnerUnion = new HashSet<>();
      Iterator <HashSet> loopIteratorOverMember = hsMember.iterator();
       while (loopIteratorOverMember.hasNext()) {
           HashSet hsInnerMemb = new HashSet<>(loopIteratorOverMember.next());
           hsInnerUnion.addAll(hsInnerMemb);
        }



       if (hsInnerUnion.size()==nextSize) {
           HashSet hsTemp = new HashSet<>(hsInnerUnion);
           hsSetOfkNbrdCombinationsets.add(hsTemp);

       }
       hsInnerUnion.clear();
       }
       return hsSetOfkNbrdCombinationsets;
      }


  public static void main(String args[]) {

   HashSet hsSetOfkNbrdCombinationsets = new HashSet<>();//should this have nested <HashSet> tokens?
   HashSet hsSetOfNxtSizeNbrdCombinationsets = new HashSet<>();//should this have nested <HashSet> tokens?

   Integer innerSize=0,nextSize  = 0;


   System.out.println("Ahoy");

   //pretend we are looping through lines in a file here
   for(String line : csvStrings)
        {
            String[] linePieces = line.split(",");
            List<String> csvPieces = new ArrayList<String>(linePieces.length);
            for(String piece : linePieces)
            {   
                //System.out.println(piece); will print each piece in separate lines
                csvPieces.add(piece);
            }
            innerSize = csvPieces.size();
            Set<String> hsInner =  new HashSet<String>(csvPieces);
            hsSetOfkNbrdCombinationsets.add(hsInner);

        }

   nextSize = innerSize+1; //programmatically obtain nextSize



   hsSetOfNxtSizeNbrdCombinationsets = SetOfNxtSizeNbrdCombinationsets(hsSetOfkNbrdCombinationsets,nextSize);
   hsSetOfkNbrdCombinationsets = SetOfkNbrdCombinationsets(hsSetOfNxtSizeNbrdCombinationsets, nextSize);

   System.out.println("The " + nextSize  + " sized combinations from elements present in the input combinations are: " + hsSetOfkNbrdCombinationsets);


} //end of main
} //end of class