Как я могу создать списки элементов размера k, если мне дано несколько комбинаций, каждая из которых имеет k-1 элементов? - PullRequest
0 голосов
/ 25 сентября 2018

В некотором смысле, это обратная задача генерации подмножеств размера k из массива, содержащего k + 1 элементов.

Например, если кто-то дает мне пары {a, b},{a, c}, {b, c}, {a, e}, {b, e}, {a, f}, мне нужен алгоритм, который скажет мне триплеты {a, b, c} и (a, b, e} полностью покрыты для их парных комбинаций в данных мне парах. Мне нужно обобщить из пары / триплета в моем примере на случай k / k + 1

Я догадывался, чтобыть хорошо документированным и эффективным алгоритмом, который решает мою проблему. К сожалению, поиск в Интернете не помог получить его. Вопросы, уже размещенные в stackoverflow, не охватывают эту проблему. Поэтому я вынужден опубликовать этот вопрос, чтобы найти свое решение.

Ответы [ 2 ]

0 голосов
/ 08 октября 2018

Функция для генерации SetOfkNbrdElementCombination // для генерации выходных данных со значениями k, превышающими два (попарно)

  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
0 голосов
/ 29 сентября 2018

Я не знаком с установленным алгоритмом для этого, и вы не просили конкретный язык, поэтому я написал алгоритм C #, который выполняет то, что вы просили, и соответствует предоставленным тестовым значениям.Это не имеет много реальной проверки ошибок.У меня есть .Net скрипка, которую вы можете запустить, чтобы увидеть результаты в веб-браузере.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.

Я включил версии для MakeTeamOf3 и MakeTeamOf4, которые показывают статические структуры циклов, которые очень легко адаптируются, когда вы знаете,Ваше 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;
        }
    }
}
...