Создать большой двумерный массив - PullRequest
0 голосов
/ 05 марта 2012

простой вопрос:

Как я могу использовать огромный двумерный массив в C #? Я хочу сделать следующее:

int[] Nodes = new int[1146445];

int[,] Relations = new int[Nodes.Lenght,Nodes.Lenght];

Это просто показывает, что я получил ошибку нехватки памяти.

Есть ли возможность работать с такими большими данными в памяти? (4 ГБ ОЗУ и 6-ядерный процессор) ^^

Целые числа, которые я хочу сохранить в двумерном массиве, маленькие. Я думаю, от 0 до 1000.

Обновление: Я пытался сохранить отношения, используя Dictionary<KeyValuePair<int, int>, int>. Это работает для некоторых добавляющих циклов. Вот класс, который должен создать график. Экземпляр CreateGraph получает свои данные из потокового ридера xml.

Main (C # backgroundWorker_DoWork)

ReadXML Reader = new ReadXML(tBOpenFile.Text);
CreateGraph Creater = new CreateGraph();

int WordsCount = (int)nUDLimit.Value;
if (nUDLimit.Value == 0) WordsCount = Reader.CountWords();

// word loop
for (int Position = 0; Position < WordsCount; Position++)
{
    // reading and parsing
    Reader.ReadNextWord();

    // add to graph builder
    Creater.AddWord(Reader.CurrentWord, Reader.GetRelations(Reader.CurrentText));
}

string[] Words = Creater.GetWords();
Dictionary<KeyValuePair<int, int>, int> Relations = Creater.GetRelations();

ReadXml

class ReadXML
{
    private string Path;
    private XmlReader Reader;
    protected int Word;
    public string CurrentWord;
    public string CurrentText;

    public ReadXML(string FilePath)
    {
        Path = FilePath;
        LoadFile();
        Word = 0;
    }

    public int CountWords()
    {
        // caching
        if(Path.Contains("filename") == true) return 1000;

        int Words = 0;
        while (Reader.Read())
        {
            if (Reader.NodeType == XmlNodeType.Element & Reader.Name == "word")
            {
                Words++;
            }
        }

        LoadFile();

        return Words;
    }

    public void ReadNextWord()
    {
        while(Reader.Read())
        {
            if(Reader.NodeType == XmlNodeType.Element & Reader.Name == "word")
            {
                while (Reader.Read())
                {
                    if (Reader.NodeType == XmlNodeType.Element & Reader.Name == "name")
                    {
                        XElement Title = XElement.ReadFrom(Reader) as XElement;
                        CurrentWord = Title.Value;

                        break;
                    }
                }
                while(Reader.Read())
                {
                    if (Reader.NodeType == XmlNodeType.Element & Reader.Name == "rels")
                    {
                        XElement Text = XElement.ReadFrom(Reader) as XElement;
                        CurrentText = Text.Value;

                        break;
                    }
                }
                break;
            }
        }
    }

    public Dictionary<string, int> GetRelations(string Text)
    {
        Dictionary<string, int> Relations = new Dictionary<string,int>();

        string[] RelationStrings = Text.Split(';');

        foreach (string RelationString in RelationStrings)
        {
            string[] SplitString = RelationString.Split(':');

            if (SplitString.Length == 2)
            {
                string RelationName = SplitString[0];
                int RelationWeight = Convert.ToInt32(SplitString[1]);

                Relations.Add(RelationName, RelationWeight);
            }
        }

        return Relations;
    }

    private void LoadFile()
    {
        Reader = XmlReader.Create(Path);
        Reader.MoveToContent();
    }
}

CreateGraph

class CreateGraph
{
    private Dictionary<string, int> CollectedWords = new Dictionary<string, int>();
    private Dictionary<KeyValuePair<int, int>, int> CollectedRelations = new Dictionary<KeyValuePair<int, int>, int>();

    public void AddWord(string Word, Dictionary<string, int> Relations)
    {
        int SourceNode = GetIdCreate(Word);
        foreach (KeyValuePair<string, int> Relation in Relations)
        {
            int TargetNode = GetIdCreate(Relation.Key);
            CollectedRelations.Add(new KeyValuePair<int,int>(SourceNode, TargetNode), Relation.Value);  // here is the error located
        }
    }

    public string[] GetWords()
    {
        string[] Words = new string[CollectedWords.Count];

        foreach (KeyValuePair<string, int> CollectedWord in CollectedWords)
        {
            Words[CollectedWord.Value] = CollectedWord.Key;
        }

        return Words;
    }

    public Dictionary<KeyValuePair<int,int>,int> GetRelations()
    {
        return CollectedRelations;
    }

    private int WordsIndex = 0;
    private int GetIdCreate(string Word)
    {
        if (!CollectedWords.ContainsKey(Word))
        {
            CollectedWords.Add(Word, WordsIndex);
            WordsIndex++;
        }
        return CollectedWords[Word];
    }

}

Теперь я получаю еще одну ошибку: элемент с таким же ключом уже существует. (На Add в CreateGraph классе.)

Ответы [ 3 ]

2 голосов
/ 05 марта 2012

Хорошо, теперь мы знаем, что вы действительно пытаетесь сделать ...

int[] Nodes = new int[1146445];

int[,] Relations = new int[Nodes.Length ,Nodes.Length];

Вы пытаетесь выделить один объект, который имеет 1,314,336,138,025 элементов, каждый из которыхразмер 4 байта.Это более 5000 ГБ.Как именно вы ожидали, что это сработает?

Что бы вы ни делали, вам явно не хватит физической памяти для такого количества элементов ... даже если CLR позволит вам выделить один объект такого размера.

Давайте возьмем небольшой пример из 50000, где вы получите ~ 9 ГБ необходимого пространства.Я не могу вспомнить, каков текущий предел (который зависит от номера версии CLR и от того, используете ли вы 32- или 64-разрядную версию CLR), но я не думаю, что кто-либо из них это поддержит.

Вы можете разбить ваш массив на «строки», как показано в ответе Хенка - это займет в целом больше памяти, но каждый массив будет достаточно мал, чтобы справиться с в в CLR.Это не поможет вам вписать все это в память, хотя в лучшем случае вы в конечном итоге перейдете к забвению.где вы выделяете место только для элементов, к которым вам действительно нужно получить доступ (или как-то приблизительно)?Или отобразить данные на диск?Если вы дадите нам больше контекста, мы сможем найти решение.

2 голосов
/ 05 марта 2012

У вас будет больше шансов, если вы установите Relations в виде зубчатого массива (массива массива):

//int[,] Relations = new int[Nodes.Length,Nodes.Length];
int[][] Relations = new int[Nodes.length] [];
for (int i = 0; i < Relations.Length; i++)
    Relations[i] = new int[Nodes.Length];

И тогда вам все равно понадобится 10k * 10k * sizeof (int) =400M

Что должно быть возможно даже при работе в 32 битах.

Обновление:

С новым номером это 1M * 1M * 4 = 4 ТБ, которое «не сработает».
И использование short для замены int приведет куменьшите его только до 2 ТБ


Поскольку вам, кажется, нужно назначить веса (разреженным) соединениям между узлами, вы должны увидеть, может ли что-то подобное работать:

struct WeightedRelation 
{ 
   public readonly int node1;
   public readonly int node2;
   public readonly int weight;
}

int[] Nodes = new int[1146445];

List<WeightedRelation> Relations = new List<WeightedRelation>();
Relations.Add(1, 2, 10);
...

Это просто основная идея, вам может понадобиться двойной словарь для быстрого поиска.Но объем вашей памяти будет пропорционален количеству фактических (не 0) отношений.

1 голос
/ 05 марта 2012

Джон и Хенк ссылались на разреженные массивы;это было бы полезно, если многие ваши узлы не связаны друг с другом.Даже если все узлы связаны со всеми остальными, вам может не понадобиться массив n by n.

Например, возможно, узлы не могут быть связаны с самим собой.Возможно, для заданных узлов x и y «x относится к y» - это то же самое, что «y связано с x».Если оба они верны, то для 4 узлов у вас есть только 6 отношений, а не 16:

a <-> b
a <-> c
a <-> d
b <-> c
b <-> d
c <-> d

В этом случае массив n-by-n тратит несколько больше половины своего пространства.,Если большое количество узлов не связано друг с другом, вы тратите впустую гораздо больше половины пространства.

Одним из быстрых способов реализовать это было бы как Dictionary<KeyType, RelationType>, где ключ однозначно идентифицируетдва узла связаны между собой.В зависимости от ваших конкретных потребностей, это может принять одну из нескольких различных форм.Вот пример, основанный на узлах и отношениях, определенных выше:

Dictionary<KeyType, Relation> x = new Dictionary<KeyType, RelationType>();
x.Add(new KeyType(a, b), new RelationType(a, b));
x.Add(new KeyType(a, c), new RelationType(a, c));
... etc.

Если отношения рефлексивны, то KeyType должен гарантировать, что new KeyType(b, a) создает объект, эквивалентный объекту, созданному new KeyType(a, b).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...