Je souhaite réduire la complexité temporelle de ce code ci-dessous à l'aide du dictionnaire (ou éventuellement d'une autre structure de données).
Pour autant que je sache, ma solution de force brute a une complexité temporelle de O (n ^ 2)
, cependant, pourrait éventuellement être fait en O (n)
(en n fois de boucle non imbriquée).
La tâche consiste à imprimer pour pour chaque jour et lieu, le pourcentage d'observations ce jour-là et lieu qui sont des observations de mammifères.
Day: 22.02.2020 Location: Backyard Percentage of Mammals in location and day: 66,67% Location: Sky Percentage of Mammals in location and day: 50,00% Day: 23.02.2020 Location: City Percentage of Mammals in location and day: 100,00% Day: 24.02.2020 Location: City Percentage of Mammals in location and day: 40,00% Day: 25.02.2020 Location: Village Percentage of Mammals in location and day: 33,33% Location: Home Percentage of Mammals in location and day: 0,00%
L'entrée ressemble à ceci:
[ {"DateTime":"2020-02-22 10:10:15", "Location":"Backyard", "Animal": {"Species":"Camel", "IsMammal": "TRUE"}}, {"DateTime":"2020-02-22 11:10:15", "Location":"Backyard", "Animal": {"Species":"Camel", "IsMammal": "TRUE"}}, {"DateTime":"2020-02-22 12:10:15", "Location":"Backyard", "Animal": {"Species":"Ant", "IsMammal": "FALSE"}}, {"DateTime":"2020-02-22 22:10:15", "Location":"Sky", "Animal": {"Species":"Flamingo", "IsMammal": "TRUE"}}, {"DateTime":"2020-02-22 23:10:15", "Location":"Sky", "Animal": {"Species":"Bee", "IsMammal": "FALSE"}}, {"DateTime":"2020-02-23 13:11:15", "Location":"City", "Animal": {"Species":"Racoon", "IsMammal": "TRUE"}}, {"DateTime":"2020-02-24 15:10:00", "Location":"City", "Animal": {"Species":"Dog", "IsMammal": "TRUE"}}, {"DateTime":"2020-02-24 19:10:00", "Location":"City", "Animal": {"Species":"Fly", "IsMammal": "FALSE"}}, {"DateTime":"2020-02-24 19:10:15", "Location":"City", "Animal": {"Species":"Butterfly", "IsMammal": "FALSE"}}, {"DateTime":"2020-02-24 19:10:20", "Location":"City", "Animal": {"Species":"Cat", "IsMammal": "TRUE"}}, {"DateTime":"2020-02-24 19:10:30", "Location":"City", "Animal": {"Species":"Flee", "IsMammal": "FALSE"}}, {"DateTime":"2020-02-25 21:10:15", "Location":"Village", "Animal": {"Species":"Horse", "IsMammal": "TRUE"}}, {"DateTime":"2020-02-25 22:10:15", "Location":"Village", "Animal": {"Species":"Fly", "IsMammal": "FALSE"}}, {"DateTime":"2020-02-25 23:10:15", "Location":"Village", "Animal": {"Species":"Bee", "IsMammal": "FALSE"}}, {"DateTime":"2020-02-25 10:10:15", "Location":"Home", "Animal": {"Species":"Iguana", "IsMammal": "FALSE"}} ]
3 Réponses :
Il existe de nombreuses astuces vraiment insensées pour réduire la complexité du temps, mais la plupart du temps, en travaillant avec des données, vous devez vous fier à leur ordre initial. Si ce n'est pas commandé, nous pouvons le commander par une clé composite. Dans votre cas, la clé est Tuple
où Item1 est le jour de la date et Item2 est l'emplacement. Cela prendrait n * log (n), puis utiliserait un temps linéaire pour parcourir les données produisant des résultats en utilisant le temps linéaire.
Donc, en regardant vos données, elles sont déjà triées. Nous pouvons donc sauter cette partie et passer à travers. Idée de base, nous initialisons un état et vérifions s'il change, nous produisons un résultat. Dans notre cas, l'état est le jour actuel, l'emplacement actuel, et nous suivons les informations dans deux variables: total des animaux, total des mammifères.
public class AnimalObservations { public DateTime DateTime { get; set; } public string Location { get; set; } public Animal Animal { get; set; } } public class Animal { public bool IsMammal { get; set; } }
Je suppose que
public static void PrintPopulation(List<AnimalObservations> animalObservations) { if (animalObservations.Count == 0) return; var item = animalObservations[0]; string currentLocation = item.Location; DateTime currentDate = item.DateTime.Date; int totalAnimals = 1; int totalMammals = item.Animal.IsMammal ? 1 : 0; for (int i = 1; i < animalObservations.Count; i++) { item = animalObservations[i]; if (currentLocation != item.Location || currentDate != item.DateTime.Date) { PrintResult(currentDate, currentLocation, totalAnimals, totalMammals); totalMammals = 0; totalAnimals = 0; currentLocation = item.Location; currentDate = item.DateTime.Date; } totalAnimals++; totalMammals += item.Animal.IsMammal ? 1 : 0; } PrintResult(currentDate, currentLocation, totalAnimals, totalMammals); } public static void PrintResult(DateTime date, string location, int totalAnimals, int totalMammals) { Console.WriteLine($"{date} {location} {(double) totalMammals / totalAnimals}"); }
Cela suppose non seulement que les données sont chronologiques, mais aussi que les lieux ne sont jamais revisités. L'OP le saura avec certitude, mais cela ne semble pas probable, même si c'est vrai pour le petit échantillon de données.
@NeilT, il semble probable que vous regardiez le dernier enregistrement au 2020-02-25
En regardant le reste des données, je soupçonne que c'est une faute de frappe pour "2020-02-26"! Mais bien sûr, si nous pouvons supposer un pré-tri, alors oui, c'est un problème beaucoup plus simple.
ce n'est pas une faute de frappe car il devrait y avoir un résultat supplémentaire dans sa sortie. mais oui j'ai aussi pensé le résoudre en utilisant le dictionnaire initialy
@Irdis merci pour cette bonne solution! C'est bien que vous ayez donné une information précieuse sur l'approche du tri et plus tard sur la façon d'utiliser l'état pour rechercher un changement. J'aurais accepté cela si NeilT n'avait pas publié cette dernière réponse, y compris la prise en compte des données non triées.
@NeilT s'il vous plaît voir le commentaire ci-dessus.
Je pense que votre solution a en fait une complexité de O (n ^ 3)
car vous faites 3 itérations imbriquées:
Étant donné que vous avez la structure de classe suivante:
public class DayLocationComparer : EqualityComparer<ValueTuple<DateTime, string>> { public override bool Equals(ValueTuple<DateTime, string> x, ValueTuple<DateTime, string> y) => x.Item1.Date == y.Item1.Date && x.Item2 == y.Item2; public override int GetHashCode(ValueTuple<DateTime, string> x) => x.Item1.GetHashCode(); }
Vous pouvez le faire dans O (n)
en utilisant deux dictionnaires --- un pour les animaux et un pour les mammifères --- qui ont une paire jour-lieu comme clé et un compteur comme valeur
IDictionary<ValueTuple<DateTime, string>, int> animals = new Dictionary<ValueTuple<DateTime, string>, int>(new DayLocationComparer()); IDictionary<ValueTuple<DateTime, string>, int> mammals = new Dictionary<ValueTuple<DateTime, string>, int>(new DayLocationComparer()); foreach (AnimalObservation ao in aos) { ValueTuple<DateTime, string> dayLocation = new ValueTuple<DateTime, string>(ao.DateTime, ao.Location); if (!animals.ContainsKey(dayLocation)) { animals.Add(dayLocation, 1); } else { animals[dayLocation] = animals[dayLocation] + 1; } if (!mammals.ContainsKey(dayLocation) && ao.Animal.IsMammal) { mammals.Add(dayLocation, 1); } else if (!mammals.ContainsKey(dayLocation) && !ao.Animal.IsMammal) { mammals.Add(dayLocation, 0); } else if (mammals.ContainsKey(dayLocation) && ao.Animal.IsMammal) { animals[dayLocation] = animals[dayLocation] + 1; } } foreach (ValueTuple<DateTime, string> dayLocation in animals.Keys) { int nrOfAnimals = animals[dayLocation]; int nrOfMammals = mammals[dayLocation]; Console.WriteLine((double)nrOfMammals / nrOfAnimals * 100); }
Où DayLocationComparer
est un comparateur qui ignore la partie Time du DateTime
public class AnimalObservation { public DateTime DateTime { get; set; } public string Location { get; set; } public Animal Animal { get; set; } } public class Animal { public string Species { get; set; } public bool IsMammal { get; set; } }
Bien sûr, je recommanderais d'utiliser peut-être une classe pour la journée -paire de localisation pour un code plus lisible.
Merci, j'apprécie la réponse. Je n'ai pas testé cela - très probablement cela fonctionne très bien. Pour moi, cela semble un peu compliqué si je dois comparer avec la réponse acceptée. Bien que j'aie aimé que vous utilisiez EqualityComparer qui est plus générique et applique le remplacement GetHash et Equals.
Une solution consiste à utiliser un dictionnaire , avec un type de clé comprenant le jour et le lieu et un type de valeur contenant le nombre de mammifères / non-mammifères.
Essayez le code ci-dessous. Vous devrez pointer la constante de chaîne vers le haut vers l'emplacement du fichier JSON. Vous devrez également ajouter la bibliothèque NewtonSoft JSON à votre projet. Notez que j'ai remplacé les méthodes Equals et GetHashCode dans la classe utilisée comme type de clé du dictionnaire.
namespace Mammals { using System; using System.Collections.Generic; using System.IO; using System.Linq; using Newtonsoft.Json.Linq; class Program { static void Main(string[] args) { const string filePath = "C:\\temp\\mammals.json"; // Read input from JSON: string jsonInput; using (var file = new StreamReader(new FileStream(filePath, FileMode.Open))) { jsonInput = file.ReadToEnd(); } var json = JArray.Parse(jsonInput); var sightings = json.Select(j => j.ToObject<Sighting>()); // Set up dictionary, using day/location as key: var sightingDictionary = new Dictionary<SightingDayAndPlace, SightingCounter>(); // Loop through sightings in O(n): foreach (var sighting in sightings) { var sightingTimeAndPlace = sighting.GetSightingDayAndPlace; if (!sightingDictionary.ContainsKey(sightingTimeAndPlace)) { sightingDictionary.Add(sightingTimeAndPlace, new SightingCounter()); } if (sighting.IsMammal) { sightingDictionary[sightingTimeAndPlace].MammalCount++; } else { sightingDictionary[sightingTimeAndPlace].NonMammalCount++; } } // Print output: var currentDay = default(DateTime); foreach (var item in sightingDictionary) { var key = item.Key; if (key.Day != currentDay) { Console.WriteLine($"Day: {key.Day:dd.MM.yyyy}"); } Console.WriteLine($"Location: {key.Location}"); Console.WriteLine($"Percentage of Mammals in location and day: {item.Value.MammalPercentage:F}%"); } Console.ReadKey(); } private class SightingDayAndPlace { public SightingDayAndPlace(DateTime day, string location) { this.Day = day; this.Location = location; } public DateTime Day { get; } public string Location { get; } public override bool Equals(object obj) { if (obj == null || !(obj is SightingDayAndPlace that)) { return false; } return this.Day == that.Day && this.Location == that.Location; } public override int GetHashCode() { // Consider a different implementation if memory or performance is relevant. return new { this.Day, this.Location }.GetHashCode(); } } private class Sighting { public DateTime DateTime { get; set; } public string Location { get; set; } public Animal Animal { get; set; } public string Species => Animal.Species; public bool IsMammal => Animal.IsMammal; public DateTime Day => DateTime.Date; public SightingDayAndPlace GetSightingDayAndPlace => new SightingDayAndPlace(this.Day, this.Location); } private class Animal { public string Species { get; set; } public bool IsMammal { get; set; } } private class SightingCounter { public int MammalCount { get; set; } public int NonMammalCount { get; set; } public double MammalPercentage => (MammalCount / ((double)MammalCount + NonMammalCount)) * 100; } } }
Merci d'avoir répondu! Il est clair, autonome et j'apprécie l'effort d'écrire le TDD avec de petites méthodes facilement testables.