NokitaKaze

Генератор аниме-фажества

Oct 16th, 2021
546
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 30.82 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Threading;
  7. using System.Threading.Tasks;
  8. using Data;
  9. using Data.Enum;
  10. using Data.Model;
  11. using Microsoft.EntityFrameworkCore;
  12. using Microsoft.Extensions.Configuration;
  13. using Microsoft.Extensions.Hosting;
  14. using Newtonsoft.Json;
  15.  
  16. namespace DanbooruDownloader.HostedServices
  17. {
  18.     public class FindWaifuService : IHostedService
  19.     {
  20.         private const double MIN_CHAR_FRANCHISE_INTERSECT_FOR_AFFILIATION = 0.85d;
  21.  
  22.         /// <summary>
  23.         /// Фильтр фажества. Минимальный процент
  24.         /// </summary>
  25.         private const double MIN_COUNT_PERCENT_BY_ARTIST_TO_CHARACTER_FOR_FAGGING = 0.28;
  26.  
  27.         private const double MIN_COUNT_BY_ARTIST_TO_CHARACTER_FOR_FAGGING = 10;
  28.         private const double MAX_FAG_POINTS_PER_CHARACTER = 10;
  29.  
  30.         private const double FAG_POINTS_LOG10_MULTIPLIER = 0.15;
  31.  
  32.         private const int MIN_CHAR_COUNT_TO_CREATE_STAT = 10;
  33.         private const int MAX_CHARACTERS_PER_IMAGE_FOR_FAGGING_COUNT = 2;
  34.  
  35.         private readonly NFContextFactory NFContextFactory;
  36.  
  37.         // ReSharper disable once NotAccessedField.Local
  38.         private readonly IConfiguration Configuration;
  39.         private readonly CancellationTokenSource CancellationTokenSource = new CancellationTokenSource();
  40.         private CancellationToken CancellationToken => CancellationTokenSource.Token;
  41.  
  42.         private Task MainTaskItself;
  43.         private Task Count123MeanTaskItself;
  44.  
  45.         public FindWaifuService(
  46.             NFContextFactory nfContextFactory,
  47.             IConfiguration configuration
  48.         )
  49.         {
  50.             NFContextFactory = nfContextFactory;
  51.             Configuration = configuration;
  52.         }
  53.  
  54.         public Task StartAsync(CancellationToken cancellationToken)
  55.         {
  56.             MainTaskItself = Task.Run(SearchForWaifu, cancellationToken);
  57.             Count123MeanTaskItself = Task.CompletedTask; // Task.Run(CalculateMeanCount123, cancellationToken);
  58.  
  59.             return Task.CompletedTask;
  60.         }
  61.  
  62.         public async Task StopAsync(CancellationToken cancellationToken)
  63.         {
  64.             CancellationTokenSource.Cancel();
  65.             await MainTaskItself;
  66.             await Count123MeanTaskItself;
  67.         }
  68.  
  69.         #region SearchForWaifu
  70.  
  71.         private DanbooruTag[] tags;
  72.         private Dictionary<int, DanbooruTag> tagDictionary;
  73.         private Dictionary<int, int> countsByTagId;
  74.         private ICollection<int> tagArtistIds;
  75.         private ICollection<int> tagCharacterIds;
  76.         private ICollection<DanbooruTagIntersect> intersects;
  77.         private IDictionary<int, int> parentImages;
  78.  
  79.         private int GetRealImageId(int imageId)
  80.         {
  81.             return parentImages.ContainsKey(imageId) ? parentImages[imageId] : imageId;
  82.         }
  83.  
  84.         private IDictionary<int, int> characterToCopyright;
  85.         private int tagOriginalId;
  86.  
  87.         private async Task SearchForWaifu_GenerateCopyright(NFContext context)
  88.         {
  89.             var tagCopyrightIds = tags
  90.                 .Where(x => x.type == DanbooruTagType.Copyright)
  91.                 .Where(x => x.id != tagOriginalId)
  92.                 .Select(t => t.id)
  93.                 .ToHashSet();
  94.             Console.WriteLine(
  95.                 "{0}\ttagCopyrightIds.Length = {1,7}",
  96.                 DateTimeOffset.UtcNow,
  97.                 tagCopyrightIds.Count()
  98.             );
  99.             var i1 = await context
  100.                 .DanbooruTagIntersect
  101.                 .Where(x => tagCopyrightIds.Contains(x.tag_id))
  102.                 .ToArrayAsync();
  103.             Console.WriteLine(
  104.                 "{0}\tDb: i1.Length = {1,7}",
  105.                 DateTimeOffset.UtcNow,
  106.                 i1.Count()
  107.             );
  108.  
  109.             i1 = i1
  110.                 .Select(t => (art_id: GetRealImageId(t.art_id), t.tag_id))
  111.                 .Distinct()
  112.                 .Select(t => new DanbooruTagIntersect() {art_id = t.art_id, tag_id = t.tag_id})
  113.                 .ToArray();
  114.             Console.WriteLine(
  115.                 "{0}\tParented: i1.Length = {1,7}",
  116.                 DateTimeOffset.UtcNow,
  117.                 i1.Count()
  118.             );
  119.  
  120.             var franchisesByArtId = i1
  121.                 .GroupBy(t => t.art_id)
  122.                 .Select(t => (art_id: t.Key, copyrights: t.Select(t1 => t1.tag_id).Distinct().ToArray()))
  123.                 .Where(x => x.copyrights.Length == 1)
  124.                 .ToDictionary(
  125.                     t => t.art_id,
  126.                     t => t.copyrights.First()
  127.                 );
  128.             var charCounts1 = intersects
  129.                 .Where(x => tagCharacterIds.Contains(x.tag_id))
  130.                 .GroupBy(t => t.tag_id)
  131.                 .ToDictionary(
  132.                     t => t.Key,
  133.                     t => t.Count()
  134.                 );
  135.             var charCounts2 = intersects
  136.                 .Where(x => franchisesByArtId.ContainsKey(x.art_id) && tagCharacterIds.Contains(x.tag_id))
  137.                 .GroupBy(t => t.tag_id)
  138.                 .ToDictionary(
  139.                     t => t.Key,
  140.                     t => t.Count()
  141.                 );
  142.             Console.WriteLine(
  143.                 "{0}\tfranchisesByArtId.Length = {1,7}" +
  144.                 "\n\t\t\t\tcharCounts1.Length = {2,7}" +
  145.                 "\n\t\t\t\tcharCounts2.Length = {3,7}",
  146.                 DateTimeOffset.UtcNow,
  147.                 franchisesByArtId.Count(),
  148.                 charCounts1.Count(),
  149.                 charCounts2.Count()
  150.             );
  151.  
  152.             characterToCopyright = intersects
  153.                 .Where(x => tagCharacterIds.Contains(x.tag_id) && charCounts1.ContainsKey(x.tag_id))
  154.                 .Select(t =>
  155.                 {
  156.                     // ReSharper disable once ConvertToLambdaExpression
  157.                     return (charId: t.tag_id,
  158.                         copyright: franchisesByArtId.ContainsKey(t.art_id) ? franchisesByArtId[t.art_id] : -1);
  159.                 })
  160.                 .Where(x => x.copyright != -1)
  161.                 .GroupBy(t => t.charId)
  162.                 .Select(t =>
  163.                 {
  164.                     var charId = t.Key;
  165.                     var minCount = charCounts1[charId] * MIN_CHAR_FRANCHISE_INTERSECT_FOR_AFFILIATION;
  166.  
  167.                     var (copyright, count) = t
  168.                         .GroupBy(t1 => t1.copyright)
  169.                         .Select(t1 => (t1.Key, count: t1.Count()))
  170.                         .OrderByDescending(t1 => t1.count)
  171.                         .First();
  172.                     // ReSharper disable once ConvertIfStatementToReturnStatement
  173.                     if (count >= minCount)
  174.                     {
  175.                         return (charId, copyright: copyright);
  176.                     }
  177.  
  178.                     if (!charCounts2.ContainsKey(charId))
  179.                     {
  180.                         return (charId, copyright: -1);
  181.                     }
  182.  
  183.                     minCount = charCounts2[charId] * MIN_CHAR_FRANCHISE_INTERSECT_FOR_AFFILIATION;
  184.                     // ReSharper disable once ConvertIfStatementToReturnStatement
  185.                     if (count >= minCount)
  186.                     {
  187.                         return (charId, copyright: copyright);
  188.                     }
  189.  
  190.                     return (charId, copyright: -1);
  191.                 })
  192.                 .Where(x => x.copyright != -1)
  193.                 .OrderBy(t => t.charId)
  194.                 .ToDictionary(
  195.                     t => t.charId,
  196.                     t => t.copyright
  197.                 );
  198.             Console.WriteLine(
  199.                 "{0}\tcharacterToCopyright.Length = {1,7}",
  200.                 DateTimeOffset.UtcNow,
  201.                 characterToCopyright.Count()
  202.             );
  203.         }
  204.  
  205.         private async Task SearchForWaifu_Step1()
  206.         {
  207.             Console.WriteLine(
  208.                 "{0}\tSearchForWaifu_Step1",
  209.                 DateTimeOffset.UtcNow
  210.             );
  211.             await using var context = NFContextFactory.CreateContext();
  212.             Console.WriteLine(
  213.                 "{0}\tDB connected",
  214.                 DateTimeOffset.UtcNow
  215.             );
  216.             parentImages = await context
  217.                 .DanbooruImage
  218.                 .Where(x => x.parent_id != null)
  219.                 .ToDictionaryAsync(
  220.                     t => t.id,
  221.                     t => t.parent_id!.Value
  222.                 );
  223.             Console.WriteLine(
  224.                 "{0}\tparentImages.Length = {1,7}",
  225.                 DateTimeOffset.UtcNow,
  226.                 parentImages.Count()
  227.             );
  228.  
  229.             tags = await context
  230.                 .DanbooruTag
  231.                 .ToArrayAsync();
  232.             tagDictionary = tags.ToDictionary(t => t.id, t => t);
  233.             Console.WriteLine(
  234.                 "{0}\ttags.Length = {1,7}",
  235.                 DateTimeOffset.UtcNow,
  236.                 tags.Count()
  237.             );
  238.             tagOriginalId = tags.First(x => x.tag == "original").id;
  239.  
  240.             tagArtistIds = tags
  241.                 .Where(x => x.type == DanbooruTagType.Artist)
  242.                 .Select(t => t.id)
  243.                 .ToHashSet();
  244.             tagCharacterIds = tags
  245.                 .Where(x => x.type == DanbooruTagType.Character)
  246.                 .Select(t => t.id)
  247.                 .ToHashSet();
  248.             var artistCharacterIds = tagArtistIds.Concat(tagCharacterIds).ToArray();
  249.             Console.WriteLine(
  250.                 "{0}\ttagArtistIds.Length = {1,7}" +
  251.                 "\n\t\t\t\ttagCharacterIds = {2,7}" +
  252.                 "\n\t\t\t\tOriginal tag = {3,7}",
  253.                 DateTimeOffset.UtcNow,
  254.                 tagArtistIds.Count(),
  255.                 tagCharacterIds.Count(),
  256.                 tagOriginalId
  257.             );
  258.  
  259.             var sw = Stopwatch.StartNew();
  260.             intersects = await context
  261.                 .DanbooruTagIntersect
  262.                 .Where(x => artistCharacterIds.Contains(x.tag_id))
  263.                 .ToArrayAsync();
  264.             sw.Stop();
  265.             var intersectsRawCount = intersects.Count;
  266.             Console.WriteLine(
  267.                 "{0}\tDb: intersects.Length = {1,7}",
  268.                 DateTimeOffset.UtcNow,
  269.                 intersects.Count()
  270.             );
  271.             intersects = intersects
  272.                 .Select(t => (art_id: GetRealImageId(t.art_id), t.tag_id))
  273.                 .Distinct()
  274.                 .Select(t => new DanbooruTagIntersect()
  275.                 {
  276.                     art_id = t.art_id,
  277.                     tag_id = t.tag_id,
  278.                 })
  279.                 .ToArray();
  280.             Console.WriteLine(
  281.                 "{0}\tParented: intersects.Length = {1,7}",
  282.                 DateTimeOffset.UtcNow,
  283.                 intersects.Count()
  284.             );
  285.             await SearchForWaifu_GenerateCopyright(context);
  286.  
  287.             {
  288.                 var artId1 = intersects
  289.                     .Where(x => tagArtistIds.Contains(x.tag_id))
  290.                     .Select(t => t.art_id)
  291.                     .ToHashSet();
  292.                 artId1 = intersects
  293.                     .Where(x => tagCharacterIds.Contains(x.tag_id))
  294.                     .Select(t => t.art_id)
  295.                     .Where(x => artId1.Contains(x))
  296.                     .ToHashSet();
  297.                 Console.WriteLine(
  298.                     "{0}\tClear artId1 with characters and artists = {1,7}",
  299.                     DateTimeOffset.UtcNow,
  300.                     artId1.Count()
  301.                 );
  302.  
  303.                 intersects = intersects
  304.                     .Where(x => artId1.Contains(x.art_id))
  305.                     .ToArray();
  306.             }
  307.  
  308.             ClearIntersectsFromMultipleChars();
  309.             await ClearIntersectsFromBadTags(context);
  310.  
  311.             countsByTagId = intersects
  312.                 .GroupBy(t => t.tag_id)
  313.                 .ToDictionary(
  314.                     t => t.Key,
  315.                     t => t.Count()
  316.                 );
  317.  
  318.             Console.WriteLine(
  319.                 "{0}\tGet intersects: {6}" +
  320.                 "\n\t\t\t\tArtists count = {1,6}" +
  321.                 "\n\t\t\t\tCharacter count = {2,6}" +
  322.                 "\n\t\t\t\tIntersects {3,7} ({4,7} {5:P1})" +
  323.                 "\n\t\t\t\tcountsByTagId.Length = {7,7}",
  324.                 DateTimeOffset.UtcNow,
  325.                 tagArtistIds.Count,
  326.                 tagCharacterIds.Count,
  327.                 intersects.Count,
  328.                 intersectsRawCount,
  329.                 intersects.Count * 1d / intersectsRawCount,
  330.                 sw.Elapsed,
  331.                 countsByTagId.Count()
  332.             );
  333.         }
  334.  
  335.         /// <summary>
  336.         /// Delete game_cg & Delete official art
  337.         /// </summary>
  338.         /// <param name="context"></param>
  339.         private async Task ClearIntersectsFromBadTags(NFContext context)
  340.         {
  341.             var gameCG = tags.First(x => x.tag == "game_cg").id;
  342.             var officialArt = tags.First(x => x.tag == "official_art").id;
  343.             var a = new[] {gameCG, officialArt};
  344.  
  345.             ICollection<int> badArtIds = await context
  346.                 .DanbooruTagIntersect
  347.                 .Where(x => a.Contains(x.tag_id))
  348.                 .Select(t => t.art_id)
  349.                 .Distinct()
  350.                 .ToArrayAsync();
  351.             badArtIds = badArtIds.ToHashSet();
  352.             intersects = intersects
  353.                 .Where(x => !badArtIds.Contains(x.art_id))
  354.                 .ToArray();
  355.  
  356.             Console.WriteLine(
  357.                 "{0}\tClearIntersectsFromBadTags: badArtIds.Length = {1,7}" +
  358.                 "\n\t\t\t\tintersects.Length = {2,7}",
  359.                 DateTimeOffset.UtcNow,
  360.                 badArtIds.Count(),
  361.                 intersects.Count()
  362.             );
  363.         }
  364.  
  365.         /// <summary>
  366.         /// Delete multi-characters (4+) arts
  367.         /// </summary>
  368.         private void ClearIntersectsFromMultipleChars()
  369.         {
  370.             var badArtIds = intersects
  371.                 .Where(x => tagCharacterIds.Contains(x.tag_id))
  372.                 .GroupBy(t => t.art_id)
  373.                 .Select(t => (art_id: t.Key, count: t.Count()))
  374.                 .Where(x => x.count > MAX_CHARACTERS_PER_IMAGE_FOR_FAGGING_COUNT)
  375.                 .Select(t => t.art_id)
  376.                 .ToHashSet();
  377.             intersects = intersects
  378.                 .Where(x => !badArtIds.Contains(x.art_id))
  379.                 .ToArray();
  380.  
  381.             Console.WriteLine(
  382.                 "{0}\tClearIntersectsFromMultipleChars: badArtIds.Length = {1,7}" +
  383.                 "\n\t\t\t\tintersects.Length = {2,7}",
  384.                 DateTimeOffset.UtcNow,
  385.                 badArtIds.Count(),
  386.                 intersects.Count()
  387.             );
  388.         }
  389.  
  390.         private async Task SearchForWaifu()
  391.         {
  392.             Console.WriteLine(
  393.                 "{0}\tSearchForWaifu",
  394.                 DateTimeOffset.UtcNow
  395.             );
  396.             await SearchForWaifu_Step1();
  397.             var byArts = intersects
  398.                 .GroupBy(t => t.art_id)
  399.                 .Select(t =>
  400.                 {
  401.                     var a = t.Select(t1 => t1.tag_id).ToArray();
  402.                     var artists = a.Where(tagArtistIds.Contains).ToArray();
  403.                     var characters = a.Where(tagCharacterIds.Contains).ToArray();
  404.  
  405.                     return (artId: t.Key, artists, characters);
  406.                 })
  407.                 .Where(x => x.artists.Any() && x.characters.Any())
  408.                 .ToDictionary(
  409.                     t => t.artId,
  410.                     t => t
  411.                 );
  412.             Console.WriteLine(
  413.                 "{0}\tbyArts.Length = {1,7}",
  414.                 DateTimeOffset.UtcNow,
  415.                 byArts.Count()
  416.             );
  417.  
  418.             var semiRawData = byArts
  419.                 .Values
  420.                 .SelectMany(t =>
  421.                 {
  422.                     var (_, artists, characters) = t;
  423.                     return artists
  424.                         .SelectMany(t1 => characters.Select(t2 => (artist: t1, character: t2)));
  425.                 })
  426.                 .GroupBy(t => t)
  427.                 .Select(t => (key: t.Key, count: t.Count()))
  428.                 .Select(t => (key: t.key, count: t.count, countP: t.count * 1d / countsByTagId[t.key.artist]))
  429.                 .ToArray();
  430.             var countDictionary = semiRawData
  431.                 .Where(x => x.count > MIN_COUNT_BY_ARTIST_TO_CHARACTER_FOR_FAGGING)
  432.                 .OrderByDescending(t => t.countP + Math.Log10(t.count) * FAG_POINTS_LOG10_MULTIPLIER)
  433.                 .ToDictionary(
  434.                     t => t.key,
  435.                     t => t.count
  436.                 );
  437.             Console.WriteLine(
  438.                 "{0}\tsemiRawData.Length = {1,7}" +
  439.                 "\n\t\t\t\tcountDictionary = {2,7}",
  440.                 DateTimeOffset.UtcNow,
  441.                 semiRawData.Count(),
  442.                 countDictionary.Count()
  443.             );
  444.  
  445.             {
  446.                 // ReSharper disable once NotAccessedVariable
  447.                 var realCount = 0;
  448.                 var lines = new List<string>();
  449.                 foreach (var ((artist, character), count) in countDictionary /*.TakeWhile(_ => realCount < 5000)*/)
  450.                 {
  451.                     var artistCount = countsByTagId[artist];
  452.                     var characterCount = countsByTagId[character];
  453.                     if (characterCount - count < MIN_COUNT_BY_ARTIST_TO_CHARACTER_FOR_FAGGING)
  454.                     {
  455.                         continue;
  456.                     }
  457.  
  458.                     if (count < artistCount * 0.1)
  459.                     {
  460.                         continue;
  461.                     }
  462.  
  463.                     realCount++;
  464.  
  465.                     lines.Add(string.Format(
  466.                         "+------T------+\tartist = {0,-30}\tcharacter = {1}",
  467.                         tagDictionary[artist].tag,
  468.                         tagDictionary[character].tag
  469.                     ));
  470.                     lines.Add(string.Format(
  471.                         "|{0,6}|{1,6}|",
  472.                         0,
  473.                         artistCount - count
  474.                     ));
  475.                     lines.Add("+------+------+");
  476.                     lines.Add(string.Format(
  477.                         "|{0,6}|{1,6}|",
  478.                         characterCount - count,
  479.                         count
  480.                     ));
  481.                     lines.Add("+------+------+");
  482.                     lines.Add(string.Format(
  483.                         "Arts by the artist to this character: {0,-6:P1}\n" +
  484.                         "Arts with the char by this artist:    {1,-6:P1}",
  485.                         count * 1d / artistCount,
  486.                         count * 1d / characterCount
  487.                     ));
  488.  
  489.                     lines.Add("=============================================");
  490.                 }
  491.  
  492.                 await File.WriteAllLinesAsync(@"waifu-list.txt", lines, CancellationToken);
  493.  
  494.                 Console.WriteLine(
  495.                     "{0}\twaifu-list.txt done",
  496.                     DateTimeOffset.UtcNow
  497.                 );
  498.             }
  499.  
  500.             {
  501.                 // 1й, 2й, 3й персонаж для каждого художника
  502.                 var count123 = semiRawData
  503.                     .GroupBy(t => t.key.artist)
  504.                     .Select(t =>
  505.                     {
  506.                         var sum = t.Sum(t1 => t1.count);
  507.                         if (sum < MIN_CHAR_COUNT_TO_CREATE_STAT)
  508.                         {
  509.                             return Array.Empty<double>();
  510.                         }
  511.  
  512.                         var r = 1d / sum;
  513.                         var a = t
  514.                             .OrderByDescending(t1 => t1.count)
  515.                             .Take(3)
  516.                             .Select(t1 => t1.count * r)
  517.                             .ToArray();
  518.  
  519.                         return a.Length switch
  520.                         {
  521.                             3 => a,
  522.                             1 => a.Concat(new[] {0d, 0d}).ToArray(),
  523.                             2 => a.Concat(new[] {0d}).ToArray(),
  524.                             _ => throw new ArgumentOutOfRangeException()
  525.                         };
  526.                     })
  527.                     .Where(x => x.Any())
  528.                     .ToArray();
  529.                 Console.WriteLine(
  530.                     "{0}\tcount123.Length = {1,7}",
  531.                     DateTimeOffset.UtcNow,
  532.                     count123.Count()
  533.                 );
  534.  
  535.                 // TODO 1й, 2й, 3й персонаж для каждого художника
  536.                 var textInput = JsonConvert.SerializeObject(count123);
  537.                 await File.WriteAllTextAsync(@"count123-input.json", textInput);
  538.             }
  539.  
  540.             {
  541.                 var rawCharFag = semiRawData
  542.                     .Where(x => (x.count >= MIN_COUNT_BY_ARTIST_TO_CHARACTER_FOR_FAGGING) &&
  543.                                 (x.countP >= MIN_COUNT_PERCENT_BY_ARTIST_TO_CHARACTER_FOR_FAGGING))
  544.                     .Select(t => (fagPoint: t.countP + Math.Log10(t.count) * FAG_POINTS_LOG10_MULTIPLIER,
  545.                         t.key.character))
  546.                     .ToArray();
  547.  
  548.                 var fagCharacters = rawCharFag
  549.                     .GroupBy(t => t.character)
  550.                     .Select(t => (character: t.Key, sum: t.Sum(t1 => t1.fagPoint), count: t.Count()))
  551.                     .OrderByDescending(t => t.sum)
  552.                     .ToArray();
  553.                 Console.WriteLine(
  554.                     "{0}\trawCharFag.Length = {1,7}" +
  555.                     "\n\t\t\t\tfagCharacters = {2,7}",
  556.                     DateTimeOffset.UtcNow,
  557.                     rawCharFag.Count(),
  558.                     fagCharacters.Count()
  559.                 );
  560.  
  561.                 var lines = new List<string>();
  562.                 foreach (var (charId, sum, count) in fagCharacters)
  563.                 {
  564.                     var character = tagDictionary[charId];
  565.                     lines.Add(string.Format("{0}\t{1:F2}\t{2}", character.tag, sum, count));
  566.                 }
  567.  
  568.                 await File.WriteAllLinesAsync(@"character-fag-list.txt", lines, CancellationToken);
  569.                 Console.WriteLine(
  570.                     "{0}\tfag-list.txt done",
  571.                     DateTimeOffset.UtcNow
  572.                 );
  573.  
  574.                 //
  575.                 var franchiseFagDic = fagCharacters
  576.                     .Select(t =>
  577.                     {
  578.                         var (charId, fagPoint, _) = t;
  579.                         if (!characterToCopyright.ContainsKey(charId))
  580.                         {
  581.                             return (copyright: -1, fagPoint: 0d);
  582.                         }
  583.  
  584.                         var copyright = characterToCopyright[charId];
  585.  
  586.                         return (copyright, fagPoint);
  587.                     })
  588.                     .Where(x => x.copyright != -1)
  589.                     .GroupBy(t => t.copyright)
  590.                     .ToDictionary(
  591.                         t => t.Key,
  592.                         t => (
  593.                             tag: tagDictionary[t.Key],
  594.                             rawSum: t.Sum(t1 => t1.fagPoint),
  595.                             limitedSum: t.Sum(t1 => Math.Min(MAX_FAG_POINTS_PER_CHARACTER, t1.fagPoint)),
  596.                             charCount: t.Count()
  597.                         )
  598.                     );
  599.  
  600.                 var lines1 = franchiseFagDic
  601.                     .OrderByDescending(t => t.Value.rawSum)
  602.                     .Take(100)
  603.                     .Select(t =>
  604.                     {
  605.                         var fagPoint = t.Value.rawSum;
  606.  
  607.                         return string.Format("{0}\t{1:F2}\t{2}", t.Value.tag.tag, fagPoint, t.Value.charCount);
  608.                     })
  609.                     .ToArray();
  610.                 await File.WriteAllLinesAsync(@"franchise-fag-list1.txt", lines1, CancellationToken);
  611.  
  612.                 var lines2 = franchiseFagDic
  613.                     .OrderByDescending(t => t.Value.limitedSum)
  614.                     .Take(100)
  615.                     .Select(t =>
  616.                     {
  617.                         var fagPoint = t.Value.limitedSum;
  618.  
  619.                         return string.Format("{0}\t{1:F2}\t{2}", t.Value.tag.tag, fagPoint, t.Value.charCount);
  620.                     })
  621.                     .ToArray();
  622.                 await File.WriteAllLinesAsync(@"franchise-fag-list2.txt", lines2, CancellationToken);
  623.  
  624.                 Console.WriteLine(
  625.                     "{0}\tfranchise-fag-list [both].txt done",
  626.                     DateTimeOffset.UtcNow
  627.                 );
  628.             }
  629.         }
  630.  
  631.         #endregion
  632.  
  633.         #region CalculateMeanCount123
  634.  
  635.         private async Task CalculateMeanCount123()
  636.         {
  637.             List<(double[] v, int count)[]> count123Chunks;
  638.             const int CHUNK_SIZE = 1000;
  639.             const int MIN_CHUNK_SIZE = 500;
  640.             {
  641.                 var text = await File.ReadAllTextAsync(@"count123-input.json");
  642.                 var rawCount123 = JsonConvert.DeserializeObject<double[][]>(text);
  643.                 var rnd = new Random();
  644.  
  645.                 var lists = new List<(double[] v, int count)[]>();
  646.                 var currentList = new List<(double[] v, int count)>();
  647.                 foreach (var values in rawCount123.OrderBy(_ => rnd.NextDouble()))
  648.                 {
  649.                     currentList.Add((values, 1));
  650.                     if (currentList.Count() >= CHUNK_SIZE)
  651.                     {
  652.                         lists.Add(currentList
  653.                             .OrderByDescending(t => t.v[0])
  654.                             .ThenByDescending(t => t.v[1])
  655.                             .ToArray());
  656.                         currentList = new List<(double[] v, int count)>();
  657.                     }
  658.                 }
  659.  
  660.                 if (currentList.Any())
  661.                 {
  662.                     lists.Add(currentList
  663.                         .OrderByDescending(t => t.v[0])
  664.                         .ThenByDescending(t => t.v[1])
  665.                         .ToArray());
  666.                 }
  667.  
  668.                 count123Chunks = lists.ToList();
  669.             }
  670.  
  671.             var fullCount = count123Chunks.Sum(t => t.Sum(t1 => t1.count));
  672.  
  673.             (double[] v, int count)[] count123 = count123Chunks.First();
  674.             count123Chunks.RemoveAt(0);
  675.             var sw = Stopwatch.StartNew();
  676.  
  677.             while (count123.Length > 2)
  678.             {
  679.                 var len = count123.Length;
  680.                 Console.WriteLine(
  681.                     "\ncount123.Length = {0} + {1,4} * {2,5}",
  682.                     count123.Length,
  683.                     count123Chunks.Count(),
  684.                     CHUNK_SIZE
  685.                 );
  686.  
  687.                 var calculatedPairs = Enumerable
  688.                     .Range(1, len - 1)
  689.                     .SelectMany(index1 =>
  690.                     {
  691.                         return Enumerable
  692.                             .Range(0, index1)
  693.                             .Select(index2 => (index1, index2));
  694.                     })
  695.                     .AsParallel()
  696.                     .Select(t =>
  697.                     {
  698.                         var (index1, index2) = t;
  699.                         // ReSharper disable once AccessToModifiedClosure
  700.                         var item1 = count123[index1];
  701.                         // ReSharper disable once AccessToModifiedClosure
  702.                         var item2 = count123[index2];
  703.  
  704.                         var sqrt = 0d;
  705.                         for (var i = 0; i < 3; i++)
  706.                         {
  707.                             sqrt += Math.Pow(item1.v[i] - item2.v[i], 2);
  708.                         }
  709.  
  710.                         sqrt = Math.Sqrt(sqrt / 3d);
  711.  
  712.                         return (index1: index1, index2: index2, sqrt);
  713.                     })
  714.                     .OrderBy(t => t.sqrt)
  715.                     .ToArray();
  716.  
  717.                 var bestPair = calculatedPairs.First();
  718.                 Console.WriteLine(bestPair);
  719.                 if (bestPair.sqrt > 0.05)
  720.                 {
  721.                     break;
  722.                 }
  723.  
  724.                 {
  725.                     var item1 = count123[bestPair.index1];
  726.                     var item2 = count123[bestPair.index2];
  727.  
  728.                     var r1 = item1.count * 1d / (item1.count + item2.count);
  729.                     var r2 = 1 - r1;
  730.  
  731.                     var newItem = (
  732.                         v: Enumerable.Range(0, 3).Select(i => item1.v[i] * r1 + item2.v[i] * r2).ToArray(),
  733.                         count: item1.count + item2.count
  734.                     );
  735.                     Console.WriteLine("\tnew item\t{1,-5}\t{0}",
  736.                         newItem.v.Select(t => t.ToString("F3")).Aggregate((a, b) => a + ", " + b),
  737.                         newItem.count);
  738.  
  739.                     count123 = count123
  740.                         .Select((t, index) => (t, index))
  741.                         .Where(x => (x.index != bestPair.index1) && (x.index != bestPair.index2))
  742.                         .Select(t => t.t)
  743.                         .Concat(new[] {newItem})
  744.                         .OrderByDescending(t => t.count)
  745.                         .ToArray();
  746.                 }
  747.  
  748.                 if ((count123.Length <= MIN_CHUNK_SIZE) && (count123Chunks.Any()))
  749.                 {
  750.                     var elapsed = sw.Elapsed;
  751.                     var leftCount = count123.Length + count123Chunks.Count * CHUNK_SIZE;
  752.                     var p = 1d - leftCount * 1d / fullCount;
  753.                     Console.WriteLine(
  754.                         "--------------------------\t{0,-7:P1} (elapsed: {1})\tETA: {2}",
  755.                         p,
  756.                         elapsed,
  757.                         (1 - p) / p * elapsed
  758.                     );
  759.  
  760.                     var r = 1d / count123.Sum(t => t.count);
  761.                     for (int i = 0; i < Math.Min(10, count123.Length); i++)
  762.                     {
  763.                         var (v, count) = count123[i];
  764.                         Console.WriteLine("\t{1,-5}\t{0}\t{2,6:P1}",
  765.                             v.Select(t => t.ToString("F3")).Aggregate((a, b) => a + ", " + b),
  766.                             count,
  767.                             count * r
  768.                         );
  769.                     }
  770.  
  771.                     count123 = count123
  772.                         .Concat(count123Chunks.First())
  773.                         .ToArray();
  774.                     count123Chunks.RemoveAt(0);
  775.                 }
  776.             }
  777.  
  778.             Console.WriteLine("==================================================");
  779.             Console.WriteLine("==================================================");
  780.             Console.WriteLine("==================================================");
  781.             var rFullCount = 1d / fullCount;
  782.             for (int i = 0; i < Math.Min(20, count123.Length); i++)
  783.             {
  784.                 var (v, count) = count123[i];
  785.                 Console.WriteLine("\t{1,-5}\t{0}\t{2,6:P1}",
  786.                     v.Select(t => t.ToString("F3")).Aggregate((a, b) => a + ", " + b),
  787.                     count,
  788.                     count * rFullCount
  789.                 );
  790.             }
  791.         }
  792.  
  793.         #endregion
  794.     }
  795. }
Add Comment
Please, Sign In to add comment