创建大的二维数组
本文关键字:二维数组 创建 | 更新日期: 2023-09-27 18:26:40
简单问题:
如何在C#中使用一个巨大的二维数组?我想做的是:
int[] Nodes = new int[1146445];
int[,] Relations = new int[Nodes.Lenght,Nodes.Lenght];
它只是认为我记错了。
有机会在内存中处理如此大的数据吗?(4gb RAM和6核CPU)^^
我想保存在二维数组中的整数很小。我猜是从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];
}
}
现在我得到另一个错误:具有相同键的元素已经存在。(在CreateGraph
类中的Add
处。)
当您将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 TB,这是行不通的
用short
代替int
只会使降低到2 TB
由于您似乎需要为节点之间的(稀疏)连接分配权重,您应该看看这样的方法是否可行:
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)关系的数量成比例。
好吧,现在我们知道你真正想做什么了…
int[] Nodes = new int[1146445];
int[,] Relations = new int[Nodes.Length ,Nodes.Length];
您试图分配一个具有1314336138025个元素的对象,每个元素的大小为4字节。这超过了5000 GB。你到底是怎么想的?
无论你做什么,你显然都会耗尽对那么多元素的物理记忆。。。即使CLR允许您分配该大小的单个对象。
让我们举一个50000的小例子,其中您最终需要大约9GB的所需空间。我记不清当前的限制是什么(这取决于CLR版本号以及您使用的是32位还是64位CLR),但我认为它们中没有一个会支持这一点。
您可以将数组分解为"行",如Henk的回答所示,这将总共占用更多的内存,但每个数组都足够小,可以在CLR中单独处理。不过,这并不能帮助你将整件事融入记忆——充其量你最终会被遗忘。
是否可以使用稀疏数组,只为真正需要访问的元素分配空间(或近似值)?或者将数据映射到磁盘?如果你给我们更多的背景,我们可能会想出一个解决方案。
Jon和Henk提到了稀疏数组;如果您的许多节点彼此不相关,这将非常有用。即使所有节点都与所有其他节点相关,也可能不需要n乘n的数组。
例如,节点可能无法与其自身相关。也许,给定节点x和y,"x与y相关"与"y与x相关"相同。如果这两个都是真的,那么对于4个节点,您只有6个关系,而不是16:
a <-> b
a <-> c
a <-> d
b <-> c
b <-> d
c <-> d
在这种情况下,一个n乘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)
创建的对象等效。